2005-07-24 14:12:37 +00:00
/* $Id$ */
2004-08-09 17:04:08 +00:00
# include "stdafx.h"
2005-06-02 19:30:21 +00:00
# include "openttd.h"
2005-06-06 22:44:11 +00:00
# include "debug.h"
2005-07-22 07:02:20 +00:00
# include "functions.h"
2005-10-28 20:04:54 +00:00
# include "gui.h"
2004-11-25 10:47:30 +00:00
# include "table/strings.h"
2004-12-15 22:18:54 +00:00
# include "map.h"
2005-01-29 12:19:05 +00:00
# include "tile.h"
2004-08-09 17:04:08 +00:00
# include "vehicle.h"
# include "command.h"
# include "pathfind.h"
2005-01-31 11:23:10 +00:00
# include "npf.h"
2004-08-09 17:04:08 +00:00
# include "station.h"
# include "table/train_cmd.h"
# include "news.h"
# include "engine.h"
# include "player.h"
2004-11-05 23:12:33 +00:00
# include "sound.h"
2005-02-06 10:18:47 +00:00
# include "depot.h"
2005-07-04 14:58:55 +00:00
# include "debug.h"
2005-03-24 17:03:37 +00:00
# include "waypoint.h"
2005-05-02 23:59:11 +00:00
# include "vehicle_gui.h"
2005-11-18 23:41:03 +00:00
# include "train.h"
2004-08-09 17:04:08 +00:00
2004-12-21 16:00:14 +00:00
static bool TrainCheckIfLineEnds ( Vehicle * v ) ;
2005-06-06 22:44:11 +00:00
static void TrainController ( Vehicle * v ) ;
2004-08-09 17:04:08 +00: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 15:37:00 +00: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 .
*/
2005-12-14 06:20:23 +00:00
static void TrainCargoChanged ( Vehicle * v )
2005-11-14 19:48:04 +00:00
{
2005-06-05 15:37:00 +00:00
Vehicle * u ;
uint16 weight = 0 ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
const RailVehicleInfo * rvi = RailVehInfo ( u - > engine_type ) ;
uint16 vweight = 0 ;
vweight + = ( _cargoc . weights [ u - > cargo_type ] * u - > cargo_count ) / 16 ;
2005-11-05 16:07:26 +00:00
// Vehicle weight is not added for articulated parts.
2005-11-18 23:41:03 +00:00
if ( ! IsArticulatedPart ( u ) ) {
2005-11-05 16:07:26 +00:00
// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
vweight + = rvi - > weight ;
// powered wagons have extra weight added
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) )
vweight + = RailVehInfo ( v - > engine_type ) - > pow_wag_weight ;
}
2005-06-05 15:37:00 +00: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 14:26:15 +00: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 15:37:00 +00:00
*/
2005-11-14 19:48:04 +00:00
void TrainConsistChanged ( Vehicle * v )
{
2005-06-06 14:26:15 +00:00
const RailVehicleInfo * rvi_v ;
2005-06-05 15:37:00 +00:00
Vehicle * u ;
uint16 max_speed = 0xFFFF ;
uint32 power = 0 ;
2005-06-06 14:26:15 +00:00
EngineID first_engine ;
assert ( v - > type = = VEH_Train ) ;
2005-11-18 23:41:03 +00:00
assert ( IsFrontEngine ( v ) | | IsFreeWagon ( v ) ) ;
2005-06-06 14:26:15 +00:00
rvi_v = RailVehInfo ( v - > engine_type ) ;
2005-11-18 23:41:03 +00:00
first_engine = IsFrontEngine ( v ) ? v - > engine_type : INVALID_VEHICLE ;
2005-11-03 09:22:24 +00:00
v - > u . rail . cached_total_length = 0 ;
2005-06-05 15:37:00 +00:00
for ( u = v ; u ! = NULL ; u = u - > next ) {
2005-06-06 00:19:24 +00:00
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
2005-06-06 22:44:11 +00:00
uint16 veh_len ;
2005-06-06 00:19:24 +00:00
2005-06-06 14:26:15 +00:00
// update the 'first engine'
u - > u . rail . first_engine = ( v = = u ) ? INVALID_VEHICLE : first_engine ;
2005-11-04 12:58:18 +00:00
if ( rvi_u - > visual_effect ! = 0 ) {
u - > u . rail . cached_vis_effect = rvi_u - > visual_effect ;
} else {
2005-11-18 23:41:03 +00:00
if ( IsTrainWagon ( u ) | | IsArticulatedPart ( u ) ) {
2005-11-05 16:07:26 +00:00
// Wagons and articulated parts have no effect by default
2005-11-04 12:58:18 +00:00
u - > u . rail . cached_vis_effect = 0x40 ;
} else if ( rvi_u - > engclass = = 0 ) {
// Steam is offset by -4 units
u - > u . rail . cached_vis_effect = 4 ;
} else {
// Diesel fumes and sparks come from the centre
u - > u . rail . cached_vis_effect = 8 ;
}
}
2005-11-18 23:41:03 +00:00
if ( ! IsArticulatedPart ( u ) ) {
2005-11-05 16:07:26 +00:00
// power is the sum of the powers of all engines and powered wagons in the consist
power + = rvi_u - > power ;
2005-06-05 15:37:00 +00:00
2005-11-05 16:07:26 +00:00
// 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 ) ) {
if ( HASBIT ( rvi_u - > callbackmask , CBM_WAGON_POWER ) ) {
uint16 callback = GetCallBackResult ( CBID_WAGON_POWER , u - > engine_type , u ) ;
if ( callback ! = CALLBACK_FAILED )
u - > u . rail . cached_vis_effect = callback ;
}
2005-06-06 00:19:24 +00:00
2005-11-05 16:07:26 +00:00
if ( u - > u . rail . cached_vis_effect < 0x40 ) {
/* wagon is powered */
SETBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ; // cache 'powered' status
power + = rvi_v - > pow_wag_power ;
}
2005-06-06 00:19:24 +00:00
}
2005-11-05 16:07:26 +00:00
// max speed is the minimum of the speed limits of all vehicles in the consist
if ( ! ( rvi_u - > flags & RVI_WAGON ) | | _patches . wagon_speed_limits )
if ( rvi_u - > max_speed ! = 0 & & ! UsesWagonOverride ( u ) )
max_speed = min ( rvi_u - > max_speed , max_speed ) ;
}
2005-06-06 22:44:11 +00: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 14:04:48 +00: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-06 22:44:11 +00:00
u - > u . rail . cached_veh_length = 8 - veh_len ;
2005-11-03 09:22:24 +00:00
v - > u . rail . cached_total_length + = u - > u . rail . cached_veh_length ;
2005-06-06 22:44:11 +00:00
2005-06-05 15:37:00 +00: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 00:19:24 +00: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 15:37:00 +00:00
}
2004-08-09 17:04:08 +00:00
2005-01-31 11:23:10 +00:00
/* These two arrays are used for realistic acceleration. XXX: How should they
* be interpreted ? */
2005-01-26 12:51:04 +00: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-11-13 13:43:55 +00:00
static bool TrainShouldStop ( const Vehicle * v , TileIndex tile )
2005-01-30 19:51:39 +00:00
{
2005-11-13 13:43:55 +00:00
const Order * o = & v - > current_order ;
2005-01-30 19:51:39 +00:00
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 19:09:04 +00:00
if ( _patches . new_nonstop & & o - > flags & OF_NON_STOP & &
2005-07-13 18:04:01 +00:00
_m [ tile ] . m2 = = o - > station )
2005-01-30 19:51:39 +00:00
return false ;
2005-07-13 18:04:01 +00:00
if ( v - > last_station_visited = = _m [ tile ] . m2 )
2005-01-30 19:51:39 +00:00
return false ;
2005-07-13 18:04:01 +00:00
if ( _m [ tile ] . m2 ! = o - > station & &
2005-03-09 19:09:04 +00:00
( o - > flags & OF_NON_STOP | | _patches . new_nonstop ) )
2005-01-30 19:51:39 +00:00
return false ;
return true ;
}
2005-01-26 12:51:04 +00:00
//new acceleration
static int GetTrainAcceleration ( Vehicle * v , bool mode )
{
2005-03-23 08:24:13 +00:00
const Vehicle * u ;
2005-01-26 12:51:04 +00: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 08:24:13 +00:00
for ( u = v ; u - > next ! = NULL ; u = u - > next , pos + + ) {
2005-01-26 12:51:04 +00:00
int dir = u - > direction ;
int ndir = u - > next - > direction ;
2005-03-23 08:24:13 +00:00
int i ;
2005-01-26 12:51:04 +00:00
for ( i = 0 ; i < 2 ; i + + ) {
if ( _curve_neighbours45 [ dir ] [ i ] = = ndir ) {
curvecount [ i ] + + ;
if ( lastpos ! = - 1 ) {
2005-03-23 08:24:13 +00:00
numcurve + + ;
sum + = pos - lastpos ;
2005-01-26 12:51:04 +00: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 19:09:04 +00:00
if ( _curve_neighbours90 [ dir ] [ 0 ] = = ndir | |
_curve_neighbours90 [ dir ] [ 1 ] = = ndir ) {
2005-01-26 12:51:04 +00:00
max_speed = 61 ;
}
}
2005-03-09 19:09:04 +00:00
if ( numcurve > 0 ) sum / = numcurve ;
2005-01-26 12:51:04 +00:00
2005-03-09 19:09:04 +00:00
if ( ( curvecount [ 0 ] ! = 0 | | curvecount [ 1 ] ! = 0 ) & & max_speed > 88 ) {
2005-01-26 12:51:04 +00:00
int total = curvecount [ 0 ] + curvecount [ 1 ] ;
2005-03-09 19:09:04 +00:00
2005-01-26 12:51:04 +00: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 ;
2005-11-18 23:41:03 +00:00
if ( IsTileType ( v - > tile , MP_STATION ) & & IsFrontEngine ( v ) ) {
2005-01-30 19:51:39 +00:00
if ( TrainShouldStop ( v , v - > tile ) ) {
2005-01-26 12:51:04 +00:00
int station_length = 0 ;
TileIndex tile = v - > tile ;
int delta_v ;
max_speed = 120 ;
do {
station_length + + ;
2005-03-23 08:24:13 +00:00
tile = TILE_ADD ( tile , TileOffsByDir ( v - > direction / 2 ) ) ;
2005-04-12 10:31:26 +00:00
} while ( IsCompatibleTrainStationTile ( tile , v - > tile ) ) ;
2005-01-26 12:51:04 +00: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 15:37:00 +00: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 12:51:04 +00:00
2005-06-05 15:37:00 +00:00
for ( u = v ; u ! = NULL ; u = u - > next ) {
2005-01-26 12:51:04 +00:00
num + + ;
drag_coeff + = 3 ;
if ( u - > u . rail . track = = 0x80 )
2005-06-01 10:30:45 +00:00
max_speed = min ( 61 , max_speed ) ;
2005-01-26 12:51:04 +00:00
2005-04-12 09:17:51 +00:00
if ( HASBIT ( u - > u . rail . flags , VRF_GOINGUP ) ) {
2005-06-05 15:37:00 +00:00
incl + = u - > u . rail . cached_veh_weight * 60 ; //3% slope, quite a bit actually
2005-04-12 09:17:51 +00:00
} else if ( HASBIT ( u - > u . rail . flags , VRF_GOINGDOWN ) ) {
2005-06-05 15:37:00 +00:00
incl - = u - > u . rail . cached_veh_weight * 60 ;
2005-01-26 12:51:04 +00:00
}
}
v - > max_speed = max_speed ;
2005-10-16 07:58:15 +00:00
if ( v - > u . rail . railtype ! = RAILTYPE_MAGLEV ) {
2005-01-26 12:51:04 +00:00
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 ) {
2005-10-16 07:58:15 +00:00
case RAILTYPE_RAIL :
case RAILTYPE_MONO :
2005-01-26 12:51:04 +00:00
force = power / speed ; //[N]
force * = 22 ;
force / = 10 ;
2005-03-09 19:09:04 +00:00
break ;
2005-10-16 07:58:15 +00:00
case RAILTYPE_MAGLEV :
2005-01-26 12:51:04 +00:00
force = power / 25 ;
2005-03-09 19:09:04 +00:00
break ;
2005-01-26 12:51:04 +00:00
}
2005-03-09 19:09:04 +00:00
} else {
2005-01-26 12:51:04 +00:00
//"kickoff" acceleration
2005-06-23 06:19:06 +00:00
force = ( mass * 8 ) + resistance ;
2005-03-09 19:09:04 +00:00
}
2005-01-26 12:51:04 +00:00
if ( force < = 0 ) force = 10000 ;
2005-10-16 07:58:15 +00:00
if ( v - > u . rail . railtype ! = RAILTYPE_MAGLEV ) force = min ( force , mass * 10 * 200 ) ;
2005-01-26 12:51:04 +00:00
if ( mode = = AM_ACCEL ) {
2005-04-12 09:21:04 +00:00
return ( force - resistance ) / ( mass * 4 ) ;
2005-01-26 12:51:04 +00:00
} else {
2005-03-09 19:09:04 +00:00
return min ( ( - force - resistance ) / ( mass * 4 ) , 10000 / ( mass * 4 ) ) ;
2005-01-26 12:51:04 +00:00
}
}
2006-01-05 12:40:50 +00:00
static void UpdateTrainAcceleration ( Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2005-03-09 19:09:04 +00:00
uint power = 0 ;
uint weight = 0 ;
2004-08-09 17:04:08 +00:00
2005-11-18 23:41:03 +00:00
assert ( IsFrontEngine ( v ) ) ;
2004-08-09 17:04:08 +00:00
2005-06-05 15:37:00 +00: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 17:04:08 +00:00
assert ( weight ! = 0 ) ;
2005-03-09 19:09:04 +00:00
v - > acceleration = clamp ( power / weight * 4 , 1 , 255 ) ;
2004-08-09 17:04:08 +00:00
}
2005-03-09 21:54:52 +00:00
int GetTrainImage ( const Vehicle * v , byte direction )
2004-08-09 17:04:08 +00:00
{
int img = v - > spritenum ;
int base ;
if ( is_custom_sprite ( img ) ) {
2005-05-05 20:46:14 +00:00
base = GetCustomVehicleSprite ( v , direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE ( img ) ) ;
2005-03-09 19:09:04 +00:00
if ( base ! = 0 ) return base ;
2005-09-26 19:01:49 +00:00
img = orig_rail_vehicle_info [ v - > engine_type ] . image_index ;
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
base = _engine_sprite_base [ img ] + ( ( direction + _engine_sprite_add [ img ] ) & _engine_sprite_and [ img ] ) ;
2005-11-14 19:48:04 +00:00
if ( v - > cargo_count > = v - > cargo_cap / 2 ) base + = _wagon_full_adder [ img ] ;
2004-08-09 17:04:08 +00:00
return base ;
}
2005-10-30 21:47:42 +00:00
extern int _traininfo_vehicle_pitch ;
2005-10-01 12:43:34 +00:00
void DrawTrainEngine ( int x , int y , EngineID engine , uint32 image_ormod )
2004-08-09 17:04:08 +00:00
{
2004-12-04 07:41:37 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( engine ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
int img = rvi - > image_index ;
uint32 image = 0 ;
if ( is_custom_sprite ( img ) ) {
2004-11-12 17:16:13 +00:00
image = GetCustomVehicleIcon ( engine , 6 ) ;
2005-10-30 21:47:42 +00:00
if ( image = = 0 ) {
img = orig_rail_vehicle_info [ engine ] . image_index ;
} else {
y + = _traininfo_vehicle_pitch ;
}
2004-08-09 17:04:08 +00:00
}
2005-03-09 19:09:04 +00:00
if ( image = = 0 ) {
2004-08-09 17:04:08 +00:00
image = ( 6 & _engine_sprite_and [ img ] ) + _engine_sprite_base [ img ] ;
}
if ( rvi - > flags & RVI_MULTIHEAD ) {
2005-03-09 19:09:04 +00:00
DrawSprite ( image | image_ormod , x - 14 , y ) ;
2004-08-09 17:04:08 +00:00
x + = 15 ;
image = 0 ;
if ( is_custom_sprite ( img ) ) {
2004-11-12 17:16:13 +00:00
image = GetCustomVehicleIcon ( engine , 2 ) ;
2005-09-26 19:01:49 +00:00
if ( image = = 0 ) img = orig_rail_vehicle_info [ engine ] . image_index ;
2004-08-09 17:04:08 +00:00
}
2005-03-09 19:09:04 +00:00
if ( image = = 0 ) {
image =
( ( 6 + _engine_sprite_add [ img + 1 ] ) & _engine_sprite_and [ img + 1 ] ) +
_engine_sprite_base [ img + 1 ] ;
2004-08-09 17:04:08 +00:00
}
}
DrawSprite ( image | image_ormod , x , y ) ;
}
2005-11-05 16:07:26 +00:00
static uint CountArticulatedParts ( const RailVehicleInfo * rvi , EngineID engine_type )
{
uint16 callback ;
uint i ;
2005-11-14 19:48:04 +00:00
if ( ! HASBIT ( rvi - > callbackmask , CBM_ARTIC_ENGINE ) ) return 0 ;
2005-11-05 16:07:26 +00:00
for ( i = 1 ; i < 10 ; i + + ) {
callback = GetCallBackResult ( CBID_ARTIC_ENGINE + ( i < < 8 ) , engine_type , NULL ) ;
2005-11-14 19:48:04 +00:00
if ( callback = = CALLBACK_FAILED | | callback = = 0xFF ) break ;
2005-11-05 16:07:26 +00:00
}
2005-11-06 10:17:20 +00:00
return i - 1 ;
2005-11-05 16:07:26 +00:00
}
static void AddArticulatedParts ( const RailVehicleInfo * rvi , Vehicle * * vl )
{
const RailVehicleInfo * rvi_artic ;
EngineID engine_type ;
Vehicle * v = vl [ 0 ] ;
Vehicle * u = v ;
uint16 callback ;
bool flip_image ;
uint i ;
if ( ! HASBIT ( rvi - > callbackmask , CBM_ARTIC_ENGINE ) )
return ;
for ( i = 1 ; i < 10 ; i + + ) {
callback = GetCallBackResult ( CBID_ARTIC_ENGINE + ( i < < 8 ) , v - > engine_type , NULL ) ;
if ( callback = = CALLBACK_FAILED | | callback = = 0xFF )
return ;
u - > next = vl [ i ] ;
u = u - > next ;
2005-11-06 10:17:20 +00:00
engine_type = GB ( callback , 0 , 7 ) ;
2005-11-05 16:07:26 +00:00
flip_image = HASBIT ( callback , 7 ) ;
rvi_artic = RailVehInfo ( engine_type ) ;
// get common values from first engine
u - > direction = v - > direction ;
u - > owner = v - > owner ;
u - > tile = v - > tile ;
u - > x_pos = v - > x_pos ;
u - > y_pos = v - > y_pos ;
u - > z_pos = v - > z_pos ;
u - > z_height = v - > z_height ;
u - > u . rail . track = v - > u . rail . track ;
u - > u . rail . railtype = v - > u . rail . railtype ;
u - > build_year = v - > build_year ;
u - > vehstatus = v - > vehstatus & ~ VS_STOPPED ;
u - > u . rail . first_engine = v - > engine_type ;
// get more settings from rail vehicle info
u - > spritenum = rvi_artic - > image_index ;
if ( flip_image ) u - > spritenum + + ;
u - > cargo_type = rvi_artic - > cargo_type ;
u - > cargo_cap = rvi_artic - > capacity ;
u - > max_speed = 0 ;
u - > max_age = 0 ;
u - > engine_type = engine_type ;
u - > value = 0 ;
u - > type = VEH_Train ;
2005-11-18 23:41:03 +00:00
u - > subtype = 0 ;
SetArticulatedPart ( u ) ;
2005-11-05 16:07:26 +00:00
u - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
u - > random_bits = VehicleRandomBits ( ) ;
2005-11-05 16:07:26 +00:00
VehiclePositionChanged ( u ) ;
}
}
2004-08-09 17:04:08 +00:00
2005-10-01 12:43:34 +00:00
static int32 CmdBuildRailWagon ( EngineID engine , TileIndex tile , uint32 flags )
2004-08-09 17:04:08 +00:00
{
int32 value ;
const RailVehicleInfo * rvi ;
2005-11-05 16:07:26 +00:00
uint num_vehicles ;
2004-08-09 17:04:08 +00:00
2005-01-04 17:11:03 +00:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2004-12-04 07:41:37 +00:00
rvi = RailVehInfo ( engine ) ;
2004-08-09 17:04:08 +00:00
value = ( rvi - > base_cost * _price . build_railwagon ) > > 8 ;
2005-11-05 16:07:26 +00:00
num_vehicles = 1 + CountArticulatedParts ( rvi , engine ) ;
2004-08-09 17:04:08 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
2005-11-06 12:39:30 +00:00
Vehicle * vl [ 11 ] ; // Allow for wagon and upto 10 artic parts.
2005-11-14 19:48:04 +00:00
Vehicle * v ;
int x ;
int y ;
2004-08-09 17:04:08 +00:00
2005-11-05 16:07:26 +00:00
if ( ! AllocateVehicles ( vl , num_vehicles ) )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
2005-01-06 22:31:58 +00:00
Vehicle * u , * w ;
2005-07-21 06:31:02 +00:00
uint dir ;
2004-08-09 17:04:08 +00:00
2005-11-05 16:07:26 +00:00
v = vl [ 0 ] ;
2005-11-14 19:48:04 +00:00
v - > spritenum = rvi - > image_index ;
2004-08-09 17:04:08 +00:00
2005-01-06 22:31:58 +00:00
u = NULL ;
2004-08-09 17:04:08 +00:00
2005-01-06 22:31:58 +00:00
FOR_ALL_VEHICLES ( w ) {
2005-06-27 06:57:24 +00:00
if ( w - > type = = VEH_Train & & w - > tile = = tile & &
2005-11-18 23:41:03 +00:00
IsFreeWagon ( w ) & & w - > engine_type = = engine ) {
2005-01-06 22:31:58 +00:00
u = GetLastVehicleInChain ( w ) ;
2004-08-09 17:04:08 +00:00
break ;
}
}
v - > engine_type = engine ;
2005-07-21 06:31:02 +00:00
dir = GB ( _m [ tile ] . m5 , 0 , 2 ) ;
2004-08-09 17:04:08 +00:00
2005-07-21 06:31:02 +00:00
v - > direction = dir * 2 + 1 ;
2005-06-27 06:57:24 +00:00
v - > tile = tile ;
2004-09-10 19:02:27 +00: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 18:04:02 +00:00
x = TileX ( tile ) * TILE_SIZE | _vehicle_initial_x_fract [ dir ] ;
y = TileY ( tile ) * TILE_SIZE | _vehicle_initial_y_fract [ dir ] ;
2004-08-09 17:04:08 +00: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-11-18 23:41:03 +00:00
v - > subtype = 0 ;
SetTrainWagon ( v ) ;
2004-08-09 17:04:08 +00:00
if ( u ! = NULL ) {
u - > next = v ;
2005-11-18 23:41:03 +00:00
} else {
SetFreeWagon ( v ) ;
2004-08-09 17:04:08 +00:00
}
v - > cargo_type = rvi - > cargo_type ;
v - > cargo_cap = rvi - > capacity ;
v - > value = value ;
// v->day_counter = 0;
2005-11-14 19:48:04 +00:00
v - > u . rail . railtype = GetEngine ( engine ) - > railtype ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
v - > build_year = _cur_year ;
v - > type = VEH_Train ;
v - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
v - > random_bits = VehicleRandomBits ( ) ;
2004-09-10 19:02:27 +00:00
2005-11-05 16:07:26 +00:00
AddArticulatedParts ( rvi , vl ) ;
2004-08-09 17:04:08 +00:00
_new_wagon_id = v - > index ;
2005-10-29 21:54:28 +00:00
_new_vehicle_id = v - > index ;
2004-08-09 17:04:08 +00:00
VehiclePositionChanged ( v ) ;
2005-06-06 14:26:15 +00:00
TrainConsistChanged ( GetFirstVehicleInChain ( v ) ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
}
return value ;
}
// Move all free vehicles in the depot to the train
2005-11-13 13:43:55 +00:00
static void NormalizeTrainVehInDepot ( const Vehicle * u )
2004-08-09 17:04:08 +00:00
{
2005-11-13 13:43:55 +00:00
const Vehicle * v ;
2005-03-09 19:09:04 +00:00
2004-08-09 17:04:08 +00:00
FOR_ALL_VEHICLES ( v ) {
2005-11-18 23:41:03 +00:00
if ( v - > type = = VEH_Train & & IsFreeWagon ( v ) & &
2004-08-09 17:04:08 +00:00
v - > tile = = u - > tile & &
v - > u . rail . track = = 0x80 ) {
2005-11-18 23:41:03 +00:00
if ( CmdFailed ( DoCommandByTile ( 0 , v - > index | ( u - > index < < 16 ) , 1 , DC_EXEC ,
CMD_MOVE_RAIL_VEHICLE ) ) )
2004-08-09 17:04:08 +00:00
break ;
}
}
}
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 ,
} ;
2005-11-13 13:43:55 +00:00
static int32 EstimateTrainCost ( const RailVehicleInfo * rvi )
2004-08-09 17:04:08 +00:00
{
return ( rvi - > base_cost * ( _price . build_railvehicle > > 3 ) ) > > 5 ;
}
2006-01-05 12:40:50 +00:00
static void AddRearEngineToMultiheadedTrain ( Vehicle * v , Vehicle * u , bool building )
2005-01-18 23:27:06 +00: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-11-18 23:41:03 +00:00
u - > subtype = 0 ;
SetMultiheaded ( u ) ;
2005-01-18 23:27:06 +00: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 19:09:04 +00:00
if ( building ) v - > value > > = 1 ;
u - > value = v - > value ;
2005-01-18 23:27:06 +00:00
u - > type = VEH_Train ;
u - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
u - > random_bits = VehicleRandomBits ( ) ;
2005-01-18 23:27:06 +00:00
VehiclePositionChanged ( u ) ;
}
2005-05-09 22:33:00 +00:00
/** Build a railroad vehicle.
* @ param x , y tile coordinates ( depot ) where rail - vehicle is built
* @ param p1 engine type id
2005-11-19 00:10:20 +00:00
* @ param p2 bit 0 prevents any free cars from being added to the train
2004-08-09 17:04:08 +00:00
*/
int32 CmdBuildRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
const RailVehicleInfo * rvi ;
2005-07-21 06:31:02 +00:00
int value ;
2005-11-05 16:07:26 +00:00
Vehicle * v ;
2005-02-04 14:24:23 +00:00
UnitID unit_num ;
2004-08-09 17:04:08 +00:00
Engine * e ;
2005-06-25 06:15:43 +00:00
TileIndex tile = TileVirtXY ( x , y ) ;
2005-11-05 16:07:26 +00:00
uint num_vehicles ;
2004-08-09 17:04:08 +00:00
2005-05-09 22:33:00 +00:00
/* Check if the engine-type is valid (for the player) */
2005-01-27 21:18:03 +00:00
if ( ! IsEngineBuildable ( p1 , VEH_Train ) ) return CMD_ERROR ;
2005-01-27 21:00:05 +00:00
2005-05-09 22:33:00 +00: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 16:58:42 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) ) return CMD_ERROR ;
2005-06-04 11:56:32 +00:00
if ( ! IsTileOwner ( tile , _current_player ) ) return CMD_ERROR ;
2005-03-06 16:58:42 +00:00
}
2005-01-29 23:58:07 +00:00
2004-08-09 17:04:08 +00:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2004-12-04 07:41:37 +00:00
rvi = RailVehInfo ( p1 ) ;
2005-07-31 13:08:08 +00:00
e = GetEngine ( p1 ) ;
/* Check if depot and new engine uses the same kind of tracks */
if ( ! IsCompatibleRail ( e - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
2004-09-10 19:02:27 +00:00
2005-05-09 22:33:00 +00:00
if ( rvi - > flags & RVI_WAGON ) return CmdBuildRailWagon ( p1 , tile , flags ) ;
2004-08-09 17:04:08 +00:00
value = EstimateTrainCost ( rvi ) ;
2005-08-01 16:31:19 +00:00
2005-11-19 00:10:20 +00:00
num_vehicles = ( rvi - > flags & RVI_MULTIHEAD ) ? 2 : 1 ;
2005-11-05 16:07:26 +00:00
num_vehicles + = CountArticulatedParts ( rvi , p1 ) ;
2004-08-09 17:04:08 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
2005-11-06 12:39:30 +00:00
Vehicle * vl [ 12 ] ; // Allow for upto 10 artic parts and dual-heads
2005-11-05 16:07:26 +00:00
if ( ! AllocateVehicles ( vl , num_vehicles ) | | IsOrderPoolFull ( ) )
2004-08-09 17:04:08 +00:00
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
2005-11-05 16:07:26 +00:00
v = vl [ 0 ] ;
2004-08-09 17:04:08 +00:00
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 06:31:02 +00:00
uint dir ;
2004-08-09 17:04:08 +00:00
v - > unitnumber = unit_num ;
2005-07-21 06:31:02 +00:00
dir = GB ( _m [ tile ] . m5 , 0 , 2 ) ;
2004-08-09 17:04:08 +00:00
2005-07-21 06:31:02 +00:00
v - > direction = dir * 2 + 1 ;
2005-06-27 06:57:24 +00:00
v - > tile = tile ;
2004-08-09 17:04:08 +00: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 16:16:43 +00:00
v - > last_station_visited = INVALID_STATION ;
2004-08-09 17:04:08 +00:00
v - > dest_tile = 0 ;
2004-09-10 19:02:27 +00:00
2005-10-01 12:43:34 +00:00
v - > engine_type = p1 ;
2004-08-09 17:04:08 +00:00
v - > reliability = e - > reliability ;
v - > reliability_spd_dec = e - > reliability_spd_dec ;
v - > max_age = e - > lifelength * 366 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
v - > string_id = STR_SV_TRAIN_NAME ;
v - > u . rail . railtype = e - > railtype ;
_new_train_id = v - > index ;
2005-10-29 21:54:28 +00:00
_new_vehicle_id = v - > index ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 ;
2005-12-28 22:29:59 +00:00
v - > random_bits = VehicleRandomBits ( ) ;
2004-08-09 17:04:08 +00:00
2005-11-18 23:41:03 +00:00
v - > subtype = 0 ;
SetFrontEngine ( v ) ;
SetTrainEngine ( v ) ;
2004-08-09 17:04:08 +00:00
VehiclePositionChanged ( v ) ;
2005-11-19 00:10:20 +00:00
if ( rvi - > flags & RVI_MULTIHEAD ) {
2005-11-18 23:41:03 +00:00
SetMultiheaded ( v ) ;
2005-11-05 16:07:26 +00:00
AddRearEngineToMultiheadedTrain ( vl [ 0 ] , vl [ 1 ] , true ) ;
2005-11-18 23:41:03 +00:00
/* Now we need to link the front and rear engines together
* other_multiheaded_part is the pointer that links to the other half of the engine
* vl [ 0 ] is the front and vl [ 1 ] is the rear
*/
vl [ 0 ] - > u . rail . other_multiheaded_part = vl [ 1 ] ;
vl [ 1 ] - > u . rail . other_multiheaded_part = vl [ 0 ] ;
2005-11-05 16:07:26 +00:00
} else {
AddArticulatedParts ( rvi , vl ) ;
2005-07-31 13:08:08 +00:00
}
2004-08-09 17:04:08 +00:00
2005-06-05 15:37:00 +00:00
TrainConsistChanged ( v ) ;
2004-08-09 17:04:08 +00:00
UpdateTrainAcceleration ( v ) ;
2005-07-31 13:08:08 +00:00
2005-11-19 00:10:20 +00:00
if ( ! HASBIT ( p2 , 0 ) ) { // check if the cars should be added to the new vehicle
2005-07-31 13:08:08 +00:00
NormalizeTrainVehInDepot ( v ) ;
}
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , tile ) ;
2004-12-10 18:16:08 +00:00
RebuildVehicleLists ( ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
2005-11-08 23:18:09 +00:00
if ( IsLocalPlayer ( ) ) {
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ; // updates the replace Train window
}
2004-08-09 17:04:08 +00:00
}
}
_cmd_build_rail_veh_score = _railveh_score [ p1 ] ;
2005-01-23 22:01:51 +00:00
2004-09-10 19:02:27 +00:00
return value ;
2004-08-09 17:04:08 +00: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 18:04:02 +00: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 19:09:04 +00:00
int CheckTrainStoppedInDepot ( const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
int count ;
TileIndex tile = v - > tile ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* check if stopped in a depot */
2005-02-06 22:36:08 +00:00
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) | | v - > cur_speed ! = 0 ) {
2004-08-09 17:04:08 +00:00
_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ;
return - 1 ;
}
count = 0 ;
2005-03-09 19:09:04 +00:00
for ( ; v ! = NULL ; v = v - > next ) {
2004-08-09 17:04:08 +00:00
count + + ;
2005-03-09 19:09:04 +00:00
if ( v - > u . rail . track ! = 0x80 | | v - > tile ! = tile | |
2005-11-18 23:41:03 +00:00
( IsFrontEngine ( v ) & & ! ( v - > vehstatus & VS_STOPPED ) ) ) {
2005-03-09 19:09:04 +00:00
_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ;
return - 1 ;
}
}
2004-08-09 17:04:08 +00:00
return count ;
}
2005-11-05 16:07:26 +00:00
/**
* Unlink a rail wagon from the consist .
* @ param v Vehicle to remove .
* @ param first The first vehicle of the consist .
* @ return The first vehicle of the consist .
*/
2004-08-09 17:04:08 +00:00
static Vehicle * UnlinkWagon ( Vehicle * v , Vehicle * first )
{
2005-05-05 20:46:14 +00:00
Vehicle * u ;
2004-08-09 17:04:08 +00:00
// unlinking the first vehicle of the chain?
if ( v = = first ) {
2005-11-05 16:07:26 +00:00
v = GetNextVehicle ( v ) ;
2005-03-09 19:09:04 +00:00
if ( v = = NULL ) return NULL ;
2005-05-05 20:46:14 +00:00
2005-11-18 23:41:03 +00:00
if ( IsTrainWagon ( v ) ) SetFreeWagon ( v ) ;
2004-08-09 17:04:08 +00:00
return v ;
}
2005-05-05 20:46:14 +00:00
2005-11-05 16:07:26 +00:00
for ( u = first ; GetNextVehicle ( u ) ! = v ; u = GetNextVehicle ( u ) ) { }
GetLastEnginePart ( u ) - > next = GetNextVehicle ( v ) ;
2005-05-05 20:46:14 +00:00
return first ;
2004-08-09 17:04:08 +00:00
}
2005-03-09 19:09:04 +00:00
static Vehicle * FindGoodVehiclePos ( const Vehicle * src )
2004-08-09 17:04:08 +00:00
{
Vehicle * dst ;
2005-10-01 12:43:34 +00:00
EngineID eng = src - > engine_type ;
2004-08-09 17:04:08 +00:00
TileIndex tile = src - > tile ;
FOR_ALL_VEHICLES ( dst ) {
2005-11-18 23:41:03 +00:00
if ( dst - > type = = VEH_Train & & IsFreeWagon ( dst ) & &
2005-03-09 19:09:04 +00:00
dst - > tile = = tile ) {
2004-08-09 17:04:08 +00:00
// check so all vehicles in the line have the same engine.
Vehicle * v = dst ;
2005-03-09 19:09:04 +00:00
2004-08-09 17:04:08 +00:00
while ( v - > engine_type = = eng ) {
2005-03-09 19:09:04 +00:00
v = v - > next ;
if ( v = = NULL ) return dst ;
2004-08-09 17:04:08 +00:00
}
}
}
return NULL ;
}
2005-11-18 23:41:03 +00:00
/*
* add a vehicle v behind vehicle dest
* use this function since it sets flags as needed
*/
static void AddWagonToConsist ( Vehicle * v , Vehicle * dest )
{
UnlinkWagon ( v , GetFirstVehicleInChain ( v ) ) ;
if ( dest = = NULL ) return ;
v - > next = dest - > next ;
dest - > next = v ;
ClearFreeWagon ( v ) ;
ClearFrontEngine ( v ) ;
}
/*
* move around on the train so rear engines are placed correctly according to the other engines
* always call with the front engine
*/
static void NormaliseTrainConsist ( Vehicle * v )
{
Vehicle * u ;
if ( IsFreeWagon ( v ) ) return ;
assert ( IsFrontEngine ( v ) ) ;
for ( ; v ! = NULL ; v = GetNextVehicle ( v ) ) {
if ( ! IsMultiheaded ( v ) | | ! IsTrainEngine ( v ) ) continue ;
/* make sure that there are no free cars before next engine */
for ( u = v ; u - > next ! = NULL & & ! IsTrainEngine ( u - > next ) ; u = u - > next ) ;
if ( u = = v - > u . rail . other_multiheaded_part ) continue ;
AddWagonToConsist ( v - > u . rail . other_multiheaded_part , u ) ;
}
}
2005-05-09 22:33:00 +00:00
/** Move a rail vehicle around inside the depot.
* @ param x , y unused
* @ param p1 various bitstuffed elements
2005-11-14 19:48:04 +00:00
* - p1 ( bit 0 - 15 ) source vehicle index
* - p1 ( bit 16 - 31 ) what wagon to put the source wagon AFTER , XXX - INVALID_VEHICLE to make a new line
2005-05-09 22:33:00 +00:00
* @ param p2 ( bit 0 ) move all vehicles following the source vehicle
2004-08-09 17:04:08 +00:00
*/
int32 CmdMoveRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
2005-10-03 21:20:01 +00:00
VehicleID s = GB ( p1 , 0 , 16 ) ;
VehicleID d = GB ( p1 , 16 , 16 ) ;
2004-08-09 17:04:08 +00:00
Vehicle * src , * dst , * src_head , * dst_head ;
2005-10-03 21:20:01 +00:00
if ( ! IsVehicleIndex ( s ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +00:00
2005-10-03 21:20:01 +00:00
src = GetVehicle ( s ) ;
2005-01-30 20:50:06 +00:00
2004-08-09 17:04:08 +00:00
if ( src - > type ! = VEH_Train ) return CMD_ERROR ;
// if nothing is selected as destination, try and find a matching vehicle to drag to.
2005-10-03 21:20:01 +00:00
if ( d = = INVALID_VEHICLE ) {
2004-08-09 17:04:08 +00:00
dst = NULL ;
2005-11-18 23:41:03 +00:00
if ( ! IsTrainEngine ( src ) ) dst = FindGoodVehiclePos ( src ) ;
2004-08-09 17:04:08 +00:00
} else {
2005-10-03 21:20:01 +00:00
dst = GetVehicle ( d ) ;
2004-08-09 17:04:08 +00:00
}
2005-11-05 16:07:26 +00:00
// if an articulated part is being handled, deal with its parent vehicle
2005-11-18 23:41:03 +00:00
while ( IsArticulatedPart ( src ) ) src = GetPrevVehicleInChain ( src ) ;
2005-11-05 16:07:26 +00:00
if ( dst ! = NULL ) {
2005-11-18 23:41:03 +00:00
while ( IsArticulatedPart ( dst ) ) dst = GetPrevVehicleInChain ( dst ) ;
2005-11-05 16:07:26 +00:00
}
2004-08-09 17:04:08 +00:00
// don't move the same vehicle..
2005-05-09 22:33:00 +00:00
if ( src = = dst ) return 0 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* the player must be the owner */
2005-11-14 19:48:04 +00:00
if ( ! CheckOwnership ( src - > owner ) | | ( dst ! = NULL & & ! CheckOwnership ( dst - > owner ) ) )
2004-08-09 17:04:08 +00:00
return CMD_ERROR ;
/* locate the head of the two chains */
src_head = GetFirstVehicleInChain ( src ) ;
dst_head = NULL ;
2005-11-05 16:07:26 +00:00
if ( dst ! = NULL ) {
dst_head = GetFirstVehicleInChain ( dst ) ;
// Now deal with articulated part of destination wagon
dst = GetLastEnginePart ( dst ) ;
}
2004-09-10 19:02:27 +00:00
2005-11-18 23:41:03 +00:00
if ( dst ! = NULL & & IsMultiheaded ( dst ) & & ! IsTrainEngine ( dst ) & & IsTrainWagon ( src ) ) {
/* We are moving a wagon to the rear part of a multiheaded engine */
if ( dst - > next = = NULL ) {
/* It's the last one, so we will add the wagon just before the rear engine */
dst = GetPrevVehicleInChain ( dst ) ;
2006-01-06 18:26:02 +00:00
/* Now if the vehicle we want to link to is the vehicle itself, drop out */
if ( dst = = src ) return CMD_ERROR ;
2005-11-18 23:41:03 +00:00
// if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that
if ( dst = = NULL ) return CMD_ERROR ;
} else {
/* there are more units on this train, so we will add the wagon after the next one*/
dst = dst - > next ;
}
}
if ( IsTrainEngine ( src ) & & dst_head ! = NULL ) {
/* we need to make sure that we didn't place it between a pair of multiheaded engines */
Vehicle * u , * engine = NULL ;
for ( u = dst_head ; u ! = NULL ; u = u - > next ) {
if ( IsTrainEngine ( u ) & & IsMultiheaded ( u ) & & u - > u . rail . other_multiheaded_part ! = NULL ) {
engine = u ;
}
if ( engine ! = NULL & & engine - > u . rail . other_multiheaded_part = = u ) {
engine = NULL ;
}
if ( u = = dst ) {
if ( engine ! = NULL ) {
dst = engine - > u . rail . other_multiheaded_part ;
}
break ;
}
2005-03-29 11:19:10 +00:00
2005-11-18 23:41:03 +00:00
}
2005-03-29 11:19:10 +00:00
}
2005-11-18 23:41:03 +00:00
if ( IsMultiheaded ( src ) & & ! IsTrainEngine ( src ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
2006-01-06 21:10:58 +00:00
{
int r , num = 0 ;
2004-08-09 17:04:08 +00:00
2006-01-06 21:10:58 +00:00
r = CheckTrainStoppedInDepot ( src_head ) ;
/* check if all vehicles in the source train are stopped inside a depot */
if ( r < 0 ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2006-01-06 21:10:58 +00:00
num + = r ;
/* check if all the vehicles in the dest train are stopped */
if ( dst_head ! = NULL ) {
r = CheckTrainStoppedInDepot ( dst_head ) ;
if ( r < 0 ) return CMD_ERROR ;
num + = r ;
assert ( dst_head - > tile = = src_head - > tile ) ;
}
/* Check that the length of the dest train is no longer than XXX vehicles */
2005-11-18 23:41:03 +00:00
if ( num > ( _patches . mammoth_trains ? 100 : 9 ) & & IsFrontEngine ( dst_head ) )
2004-08-09 17:04:08 +00:00
return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
}
// when moving all wagons, we can't have the same src_head and dst_head
2005-05-09 22:33:00 +00:00
if ( HASBIT ( p2 , 0 ) & & src_head = = dst_head ) return 0 ;
2004-08-09 17:04:08 +00:00
// moving a loco to a new line?, then we need to assign a unitnumber.
2005-11-18 23:41:03 +00:00
if ( dst = = NULL & & ! IsFrontEngine ( src ) & & IsTrainEngine ( src ) ) {
2005-02-04 14:24:23 +00:00
UnitID unit_num = GetFreeUnitNumber ( VEH_Train ) ;
2004-08-09 17:04:08 +00: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-11-18 23:41:03 +00: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-11-06 01:15:10 +00:00
2005-05-09 22:33:00 +00:00
if ( HASBIT ( p2 , 0 ) ) {
2004-08-09 17:04:08 +00:00
// unlink ALL wagons
if ( src ! = src_head ) {
Vehicle * v = src_head ;
2005-11-05 16:07:26 +00:00
while ( GetNextVehicle ( v ) ! = src ) v = GetNextVehicle ( v ) ;
GetLastEnginePart ( v ) - > next = NULL ;
2005-06-06 14:26:15 +00:00
} else {
src_head = NULL ;
2004-08-09 17:04:08 +00:00
}
} else {
2005-06-06 21:32:04 +00: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 17:04:08 +00:00
// unlink single wagon from linked list
2005-06-06 14:26:15 +00:00
src_head = UnlinkWagon ( src , src_head ) ;
2005-11-05 16:07:26 +00:00
GetLastEnginePart ( src ) - > next = NULL ;
2004-08-09 17:04:08 +00:00
}
if ( dst = = NULL ) {
2005-11-18 23:41:03 +00:00
// move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4.
if ( IsTrainEngine ( src ) ) {
if ( ! IsFrontEngine ( src ) ) {
2005-01-15 19:06:22 +00:00
// setting the type to 0 also involves setting up the orders field.
2005-11-18 23:41:03 +00:00
SetFrontEngine ( src ) ;
2005-01-15 19:06:22 +00:00
assert ( src - > orders = = NULL ) ;
2004-08-09 17:04:08 +00:00
src - > num_orders = 0 ;
}
} else {
2005-11-18 23:41:03 +00:00
SetFreeWagon ( src ) ;
2004-08-09 17:04:08 +00:00
}
2005-06-06 14:26:15 +00:00
dst_head = src ;
2004-08-09 17:04:08 +00:00
} else {
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( src ) ) {
2005-01-15 19:06:22 +00:00
// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
2004-08-09 17:04:08 +00:00
DeleteWindowById ( WC_VEHICLE_VIEW , src - > index ) ;
2005-01-15 19:06:22 +00:00
DeleteVehicleOrders ( src ) ;
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2005-11-18 23:41:03 +00:00
ClearFrontEngine ( src ) ;
ClearFreeWagon ( src ) ;
2004-08-09 17:04:08 +00:00
src - > unitnumber = 0 ; // doesn't occupy a unitnumber anymore.
// link in the wagon(s) in the chain.
{
2005-03-09 19:09:04 +00:00
Vehicle * v ;
2005-11-05 16:07:26 +00:00
for ( v = src ; GetNextVehicle ( v ) ! = NULL ; v = GetNextVehicle ( v ) ) ;
GetLastEnginePart ( v ) - > next = dst - > next ;
2004-08-09 17:04:08 +00:00
}
dst - > next = src ;
}
2005-11-18 23:41:03 +00:00
if ( src - > u . rail . other_multiheaded_part ! = NULL ) {
if ( src - > u . rail . other_multiheaded_part = = src_head ) {
src_head = src_head - > next ;
}
AddWagonToConsist ( src - > u . rail . other_multiheaded_part , src ) ;
}
if ( HASBIT ( p2 , 0 ) & & src_head ! = NULL & & src_head ! = src ) {
/* if we stole a rear multiheaded engine, we better give it back to the front end */
Vehicle * engine = NULL , * u ;
for ( u = src_head ; u ! = NULL ; u = u - > next ) {
if ( IsMultiheaded ( u ) ) {
if ( IsTrainEngine ( u ) ) {
engine = u ;
continue ;
}
/* we got the rear engine to match with the front one */
engine = NULL ;
}
}
if ( engine ! = NULL & & engine - > u . rail . other_multiheaded_part ! = NULL ) {
AddWagonToConsist ( engine - > u . rail . other_multiheaded_part , engine ) ;
// previous line set the front engine to the old front. We need to clear that
engine - > u . rail . other_multiheaded_part - > first = NULL ;
}
}
2004-08-09 17:04:08 +00:00
2005-11-06 01:15:10 +00:00
/* If there is an engine behind first_engine we moved away, it should become new first_engine
* To do this , CmdMoveRailVehicle must be called once more
2005-11-18 23:41:03 +00:00
* we can ' t loop forever here because next time we reach this line we will have a front engine */
if ( src_head ! = NULL & & ! IsFrontEngine ( src_head ) & & IsTrainEngine ( src_head ) ) {
CmdMoveRailVehicle ( x , y , flags , src_head - > index | ( INVALID_VEHICLE < < 16 ) , 1 ) ;
src_head = NULL ; // don't do anything more to this train since the new call will do it
2005-11-06 01:15:10 +00:00
}
2005-06-06 14:26:15 +00:00
if ( src_head ) {
2005-11-18 23:41:03 +00:00
NormaliseTrainConsist ( src_head ) ;
2005-06-05 15:37:00 +00:00
TrainConsistChanged ( src_head ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( src_head ) ) {
2005-06-06 14:26:15 +00:00
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 17:04:08 +00:00
if ( dst_head ) {
2005-11-18 23:41:03 +00:00
NormaliseTrainConsist ( dst_head ) ;
2005-06-06 14:26:15 +00:00
TrainConsistChanged ( dst_head ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( dst_head ) ) {
2004-08-09 17:04:08 +00:00
UpdateTrainAcceleration ( dst_head ) ;
2005-06-06 14:26:15 +00: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 15:37:00 +00:00
}
2005-06-06 14:26:15 +00:00
/* Update the depot window */
InvalidateWindow ( WC_VEHICLE_DEPOT , dst_head - > tile ) ;
2004-08-09 17:04:08 +00:00
}
2004-12-20 18:11:22 +00:00
RebuildVehicleLists ( ) ;
2004-08-09 17:04:08 +00:00
}
return 0 ;
}
2005-05-09 22:33:00 +00:00
/** Start/Stop a train.
* @ param x , y unused
* @ param p1 train to start / stop
* @ param p2 unused
*/
2004-08-09 17:04:08 +00:00
int32 CmdStartStopTrain ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
2005-01-30 20:50:06 +00:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 22:31:58 +00:00
v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2005-05-09 22:33:00 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
v - > u . rail . days_since_order_progr = 0 ;
v - > vehstatus ^ = VS_STOPPED ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
return 0 ;
}
2005-05-09 22:33:00 +00:00
/** Sell a (single) train wagon/engine.
* @ param x , y unused
2005-05-05 20:46:14 +00:00
* @ param p1 the wagon / engine index
* @ param p2 the selling mode
2005-05-09 22:33:00 +00: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 20:46:14 +00:00
*/
2004-08-09 17:04:08 +00:00
int32 CmdSellRailWagon ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
2005-05-05 20:46:14 +00:00
Vehicle * v , * tmp , * first ;
2005-11-18 23:41:03 +00:00
Vehicle * new_f = NULL ;
2005-05-05 20:46:14 +00:00
int32 cost = 0 ;
2004-08-09 17:04:08 +00:00
2005-05-05 20:46:14 +00:00
if ( ! IsVehicleIndex ( p1 ) | | p2 > 2 ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2005-01-06 22:31:58 +00:00
v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2005-05-09 22:33:00 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2005-01-30 20:50:06 +00:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2005-11-18 23:41:03 +00:00
while ( IsArticulatedPart ( v ) ) v = GetPrevVehicleInChain ( v ) ;
2005-05-05 20:46:14 +00:00
first = GetFirstVehicleInChain ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// make sure the vehicle is stopped in the depot
2005-05-09 22:33:00 +00:00
if ( CheckTrainStoppedInDepot ( first ) < 0 ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2005-11-18 23:41:03 +00:00
if ( IsMultiheaded ( v ) & & ! IsTrainEngine ( v ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
2005-05-17 23:08:21 +00:00
if ( flags & DC_EXEC ) {
2005-11-18 23:41:03 +00:00
if ( v = = first & & IsFrontEngine ( first ) ) {
2005-05-17 23:08:21 +00:00
DeleteWindowById ( WC_VEHICLE_VIEW , first - > index ) ;
2005-11-08 23:18:09 +00:00
}
if ( IsLocalPlayer ( ) & & ( p1 = = 1 | | ! ( RailVehInfo ( v - > engine_type ) - > flags & RVI_WAGON ) ) ) {
2005-05-17 23:08:21 +00:00
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
2005-05-05 20:46:14 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , first - > tile ) ;
2005-05-08 20:53:02 +00:00
RebuildVehicleLists ( ) ;
2005-05-05 20:46:14 +00:00
}
2004-08-09 17:04:08 +00:00
2005-05-05 20:46:14 +00: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 19:09:04 +00:00
2005-05-05 20:46:14 +00:00
/* 1. Delete the engine, if it is dualheaded also delete the matching
2005-11-18 23:41:03 +00:00
* rear engine of the loco ( from the point of deletion onwards ) */
Vehicle * rear = ( IsMultiheaded ( v ) & &
IsTrainEngine ( v ) ) ? v - > u . rail . other_multiheaded_part : NULL ;
2005-05-05 20:46:14 +00:00
if ( rear ! = NULL ) {
2005-11-18 23:41:03 +00:00
cost - = rear - > value ;
2005-05-05 20:46:14 +00:00
if ( flags & DC_EXEC ) {
2005-11-18 23:41:03 +00:00
UnlinkWagon ( rear , first ) ;
2005-05-05 20:46:14 +00:00
DeleteVehicle ( rear ) ;
}
}
2005-03-09 19:09:04 +00:00
2005-05-05 20:46:14 +00:00
/* 2. We are selling the first engine, some special action might be required
2005-11-18 23:41:03 +00:00
* here , so take attention */
2005-05-06 16:13:44 +00:00
if ( ( flags & DC_EXEC ) & & v = = first ) {
2005-11-18 23:41:03 +00:00
new_f = GetNextVehicle ( first ) ;
2005-05-05 20:46:14 +00:00
/* 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 ) {
2005-11-18 23:41:03 +00:00
if ( IsTrainEngine ( new_f ) ) {
2005-05-05 20:46:14 +00:00
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 */
2005-09-14 18:03:38 +00:00
if ( IsLocalPlayer ( ) ) ShowTrainViewWindow ( new_f ) ;
2005-05-05 20:46:14 +00:00
}
}
}
/* 3. Delete the requested wagon */
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
2005-03-29 11:19:10 +00:00
2005-05-05 20:46:14 +00:00
/* 4 If the second wagon was an engine, update it to front_engine
* which UnlinkWagon ( ) has changed to TS_Free_Car */
2005-11-18 23:41:03 +00:00
if ( switch_engine ) SetFrontEngine ( first ) ;
2005-05-05 20:46:14 +00:00
/* 5. If the train still exists, update its acceleration, window, etc. */
2005-06-06 14:26:15 +00:00
if ( first ! = NULL ) {
2005-11-18 23:41:03 +00:00
NormaliseTrainConsist ( first ) ;
2005-06-05 15:37:00 +00:00
TrainConsistChanged ( first ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( first ) ) {
2005-06-06 14:26:15 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , first - > index ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
UpdateTrainAcceleration ( first ) ;
}
2005-05-05 20:46:14 +00:00
}
2004-09-10 19:02:27 +00:00
2005-03-09 19:09:04 +00:00
2005-05-05 20:46:14 +00:00
/* (6.) Borked AI. If it sells an engine it expects all wagons lined
2005-11-18 23:41:03 +00:00
* up on a new line to be added to the newly built loco . Replace it is .
* Totally braindead cause building a new engine adds all loco - less
* engines to its train anyways */
if ( p2 = = 2 & & HASBIT ( ori_subtype , Train_Front ) ) {
2005-05-05 20:46:14 +00:00
for ( v = first ; v ! = NULL ; v = tmp ) {
2005-11-05 16:07:26 +00:00
tmp = GetNextVehicle ( v ) ;
2005-05-05 20:46:14 +00:00
DoCommandByTile ( v - > tile , v - > index | INVALID_VEHICLE < < 16 , 0 , DC_EXEC , CMD_MOVE_RAIL_VEHICLE ) ;
}
2004-08-09 17:04:08 +00:00
}
}
2005-05-05 20:46:14 +00:00
} break ;
case 1 : { /* Delete wagon and all wagons after it given certain criteria */
2005-11-18 23:41:03 +00:00
/* 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 */
2005-05-05 20:46:14 +00:00
for ( ; v ! = NULL ; v = tmp ) {
2005-11-05 16:07:26 +00:00
tmp = GetNextVehicle ( v ) ;
2005-05-05 20:46:14 +00:00
2005-11-18 23:41:03 +00:00
if ( IsMultiheaded ( v ) ) {
if ( IsTrainEngine ( v ) ) {
/* We got a front engine of a multiheaded set. Now we will sell the rear end too */
Vehicle * rear = v - > u . rail . other_multiheaded_part ;
if ( rear ! = NULL ) {
cost - = rear - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( rear , first ) ;
DeleteVehicle ( rear ) ;
}
}
} else if ( v - > u . rail . other_multiheaded_part ! = NULL ) {
/* The front to this engine is earlier in this train. Do nothing */
2005-10-23 13:04:44 +00:00
continue ;
}
2005-05-05 20:46:14 +00:00
}
2004-09-10 19:02:27 +00:00
2005-05-05 20:46:14 +00:00
cost - = v - > value ;
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
DeleteVehicle ( v ) ;
}
}
2004-08-09 17:04:08 +00:00
2005-06-05 15:37:00 +00:00
/* 3. If it is still a valid train after selling, update its acceleration and cached values */
2005-06-06 14:26:15 +00:00
if ( ( flags & DC_EXEC ) & & first ! = NULL ) {
2005-11-18 23:41:03 +00:00
NormaliseTrainConsist ( first ) ;
2005-06-05 15:37:00 +00:00
TrainConsistChanged ( first ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( first ) )
2005-06-06 14:26:15 +00:00
UpdateTrainAcceleration ( first ) ;
2005-11-18 23:41:03 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , first - > index ) ;
InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
2005-06-05 15:37:00 +00:00
}
2005-05-05 20:46:14 +00:00
} break ;
}
2004-08-09 17:04:08 +00: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 06:31:02 +00: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 17:04:08 +00:00
}
static void UpdateVarsAfterSwap ( Vehicle * v )
{
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
BeginVehicleMove ( v ) ;
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
}
2005-11-14 19:48:04 +00:00
static void SetLastSpeed ( Vehicle * v , int spd )
{
2004-08-09 17:04:08 +00: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-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
}
2005-01-09 16:02:06 +00: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 17:04:08 +00:00
static void ReverseTrainSwapVeh ( Vehicle * v , int l , int r )
{
Vehicle * a , * b ;
/* locate vehicles to swap */
2005-10-23 13:04:44 +00:00
for ( a = v ; l ! = 0 ; l - - ) a = a - > next ;
for ( b = v ; r ! = 0 ; r - - ) b = b - > next ;
2004-08-09 17:04:08 +00:00
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 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* swap more variables */
2005-01-25 21:43:57 +00:00
swap_int32 ( & a - > x_pos , & b - > x_pos ) ;
swap_int32 ( & a - > y_pos , & b - > y_pos ) ;
2004-08-09 17:04:08 +00:00
swap_tile ( & a - > tile , & b - > tile ) ;
swap_byte ( & a - > z_pos , & b - > z_pos ) ;
2005-01-09 16:02:06 +00:00
SwapTrainFlags ( & a - > u . rail . flags , & b - > u . rail . flags ) ;
2004-08-09 17:04:08 +00:00
/* update other vars */
UpdateVarsAfterSwap ( a ) ;
UpdateVarsAfterSwap ( b ) ;
2005-03-25 12:07:26 +00:00
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
VehicleEnterTile ( b , b - > tile , b - > x_pos , b - > y_pos ) ;
2004-08-09 17:04:08 +00:00
} else {
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction ^ = 4 ;
2004-09-10 19:02:27 +00:00
UpdateVarsAfterSwap ( a ) ;
2005-03-25 12:07:26 +00:00
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
2004-08-09 17:04:08 +00:00
}
}
2004-12-21 16:17:27 +00:00
/* Check if the vehicle is a train and is on the tile we are testing */
static void * TestTrainOnCrossing ( Vehicle * v , void * data )
{
2005-11-14 19:48:04 +00:00
if ( v - > tile ! = * ( const TileIndex * ) data | | v - > type ! = VEH_Train ) return NULL ;
2004-12-21 16:17:27 +00:00
return v ;
}
2005-01-23 10:40:54 +00:00
static void DisableTrainCrossing ( TileIndex tile )
{
2005-11-14 19:48:04 +00:00
if ( IsTileType ( tile , MP_STREET ) & &
IsLevelCrossing ( tile ) & &
VehicleFromPos ( tile , & tile , TestTrainOnCrossing ) = = NULL & & // empty?
GB ( _m [ tile ] . m5 , 2 , 1 ) ! = 0 ) { // Lights on?
SB ( _m [ tile ] . m5 , 2 , 1 , 0 ) ; // Switch lights off
MarkTileDirtyByTile ( tile ) ;
2005-01-23 10:40:54 +00:00
}
}
2005-06-06 22:44:11 +00: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 )
{
2005-11-14 19:48:04 +00:00
Vehicle * base ;
Vehicle * first ;
int length ;
2005-06-06 22:44:11 +00:00
base = v ;
first = base - > next ;
length = CountVehiclesInChain ( v ) ;
while ( length > 2 ) {
2005-11-14 19:48:04 +00:00
Vehicle * last ;
int differential ;
int i ;
2005-06-06 22:44:11 +00:00
// find pairwise matching wagon
// start<>end, start+1<>end-1, ... */
last = first ;
2005-11-14 19:48:04 +00:00
for ( i = length - 3 ; i > 0 ; i - - ) last = last - > next ;
2005-06-06 22:44:11 +00:00
differential = last - > u . rail . cached_veh_length - base - > u . rail . cached_veh_length ;
2005-11-14 19:48:04 +00:00
if ( before ) differential * = - 1 ;
2005-06-06 22:44:11 +00:00
if ( differential > 0 ) {
2005-11-14 19:48:04 +00:00
Vehicle * tempnext ;
2005-06-06 22:44:11 +00:00
// disconnect last car to make sure only this subset moves
tempnext = last - > next ;
last - > next = NULL ;
2005-11-14 19:48:04 +00:00
for ( i = 0 ; i < differential ; i + + ) TrainController ( first ) ;
2005-06-06 22:44:11 +00:00
last - > next = tempnext ;
}
base = first ;
first = first - > next ;
length - = 2 ;
}
}
2005-11-13 13:43:55 +00:00
static TileIndex GetVehicleTileOutOfTunnel ( const Vehicle * v , bool reverse )
2005-07-04 14:58:55 +00:00
{
TileIndex tile ;
byte direction = ( ! reverse ) ? DirToDiagdir ( v - > direction ) : ReverseDiagdir ( v - > direction > > 1 ) ;
TileIndexDiff delta = TileOffsByDir ( direction ) ;
2005-11-14 19:48:04 +00:00
if ( v - > u . rail . track ! = 0x40 ) return v - > tile ;
2005-07-04 14:58:55 +00:00
for ( tile = v - > tile ; ; tile + = delta ) {
2005-10-05 07:20:26 +00:00
if ( IsTunnelTile ( tile ) & & GB ( _m [ tile ] . m5 , 0 , 2 ) ! = direction & & GetTileZ ( tile ) = = v - > z_pos )
2005-07-04 14:58:55 +00:00
break ;
}
return tile ;
2005-07-23 19:48:24 +00:00
}
2005-07-04 14:58:55 +00:00
2004-08-09 17:04:08 +00:00
static void ReverseTrainDirection ( Vehicle * v )
{
int l = 0 , r = - 1 ;
Vehicle * u ;
2005-07-04 14:58:55 +00:00
TileIndex tile ;
2005-07-17 20:09:02 +00: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 14:58:55 +00:00
u = GetLastVehicleInChain ( v ) ;
tile = GetVehicleTileOutOfTunnel ( u , false ) ;
trackdir = ReverseTrackdir ( GetVehicleTrackdir ( u ) ) ;
2005-07-17 20:09:02 +00:00
if ( PBSTileReserved ( tile ) & ( 1 < < TrackdirToTrack ( trackdir ) ) ) {
2005-07-04 14:58:55 +00: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 ;
}
2005-11-14 19:48:04 +00:00
// we found a way out of the pbs block
2005-07-04 14:58:55 +00:00
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 20:09:02 +00:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 14:58:55 +00: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 20:09:02 +00:00
PBSClearPath ( tile , trackdir , pbs_end_tile , pbs_end_trackdir ) ;
2005-07-04 14:58:55 +00:00
v - > u . rail . pbs_status = PBS_STAT_NONE ;
2005-07-17 20:09:02 +00:00
} else if ( PBSTileReserved ( tile ) & ( 1 < < TrackdirToTrack ( trackdir ) ) ) {
PBSClearPath ( tile , trackdir , pbs_end_tile , pbs_end_trackdir ) ;
2005-07-04 14:58:55 +00:00
if ( v - > u . rail . track ! = 0x40 )
PBSReserveTrack ( tile , trackdir & 7 ) ;
} ;
2004-08-09 17:04:08 +00:00
2005-02-06 22:36:08 +00:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) )
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2005-07-04 14:58:55 +00:00
2004-12-21 16:02:14 +00:00
/* Check if we were approaching a rail/road-crossing */
{
TileIndex tile = v - > tile ;
int t ;
2005-03-08 19:54:10 +00:00
/* Determine the diagonal direction in which we will exit this tile */
2004-12-21 16:02:14 +00: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 13:32:03 +00:00
tile + = TileOffsByDir ( t ) ;
2005-01-23 10:40:54 +00:00
/* Check if the train left a rail/road-crossing */
DisableTrainCrossing ( tile ) ;
2004-12-21 16:02:14 +00:00
}
2004-08-09 17:04:08 +00:00
// count number of vehicles
u = v ;
do r + + ; while ( ( u = u - > next ) ! = NULL ) ;
2005-06-06 22:44:11 +00:00
AdvanceWagons ( v , true ) ;
2004-08-09 17:04:08 +00:00
/* swap start<>end, start+1<>end-1, ... */
do {
ReverseTrainSwapVeh ( v , l + + , r - - ) ;
} while ( l < = r ) ;
2005-06-06 22:44:11 +00:00
AdvanceWagons ( v , false ) ;
2005-02-06 22:36:08 +00:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) )
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2005-01-09 16:02:06 +00:00
CLRBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
2004-08-09 17:04:08 +00:00
}
2005-05-09 22:33:00 +00: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 17:04:08 +00:00
{
Vehicle * v ;
2005-01-30 20:50:06 +00:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 22:31:58 +00:00
v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2005-05-09 22:33:00 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
_error_message = STR_EMPTY ;
2005-02-06 22:36:08 +00:00
// if (v->u.rail.track & 0x80 || IsTileDepotType(v->tile, TRANSPORT_RAIL))
2004-08-09 17:04:08 +00:00
// return CMD_ERROR;
2005-05-09 22:33:00 +00:00
if ( v - > u . rail . crash_anim_pos ! = 0 | | v - > breakdown_ctr ! = 0 ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
if ( _patches . realistic_acceleration & & v - > cur_speed ! = 0 ) {
2005-01-09 16:02:06 +00:00
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
2004-08-09 17:04:08 +00:00
} else {
v - > cur_speed = 0 ;
SetLastSpeed ( v , 0 ) ;
ReverseTrainDirection ( v ) ;
}
}
return 0 ;
}
2005-05-09 22:33:00 +00: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 17:04:08 +00:00
int32 CmdForceTrainProceed ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
Vehicle * v ;
2005-01-30 20:50:06 +00:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 22:31:58 +00:00
v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2005-05-09 22:33:00 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2005-11-14 19:48:04 +00:00
if ( flags & DC_EXEC ) v - > u . rail . force_proceed = 0x50 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
return 0 ;
}
2005-05-14 12:36:16 +00:00
/** Refits a train to the specified cargo type.
* @ param x , y unused
* @ param p1 vehicle ID of the train to refit
2005-07-31 13:08:08 +00:00
* @ param p2 the new cargo type to refit to ( p2 & 0xFF )
2005-05-14 12:36:16 +00:00
*/
2004-08-09 17:04:08 +00:00
int32 CmdRefitRailVehicle ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
{
2005-11-14 08:09:57 +00:00
CargoID new_cid = GB ( p2 , 0 , 8 ) ;
2004-08-09 17:04:08 +00:00
Vehicle * v ;
int32 cost ;
uint num ;
2005-01-30 20:50:06 +00:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
2005-01-06 18:45:28 +00:00
2005-01-06 22:31:58 +00:00
v = GetVehicle ( p1 ) ;
2005-01-30 20:50:06 +00:00
2005-05-14 12:36:16 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2005-07-31 13:08:08 +00:00
if ( CheckTrainStoppedInDepot ( v ) < 0 ) return_cmd_error ( STR_TRAIN_MUST_BE_STOPPED ) ;
2005-05-14 12:36:16 +00:00
/* Check cargo */
if ( new_cid > NUM_CARGO ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2005-01-30 20:50:06 +00:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_RUN ) ;
2004-08-09 17:04:08 +00:00
cost = 0 ;
num = 0 ;
2005-01-06 18:45:28 +00:00
2004-08-09 17:04:08 +00:00
do {
2004-11-22 23:05:34 +00: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-11-29 22:29:59 +00:00
if ( ! CanRefitTo ( v - > engine_type , new_cid ) ) continue ;
2005-05-14 12:36:16 +00:00
2005-05-26 16:31:32 +00:00
if ( v - > cargo_cap ! = 0 ) {
2005-09-26 18:43:58 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
2005-06-03 10:39:30 +00: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 11:34:37 +00:00
2005-06-03 10:39:30 +00:00
if ( amount = = CALLBACK_FAILED ) { // callback failed or not used, use default
2005-06-01 11:34:37 +00: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 ) {
2005-11-14 19:48:04 +00:00
if ( new_cid ! = v - > cargo_type ) cost + = _price . build_railvehicle > > 8 ;
2005-06-01 11:34:37 +00:00
num + = amount ;
if ( flags & DC_EXEC ) {
2005-07-31 13:08:08 +00:00
v - > cargo_count = 0 ;
2005-06-01 11:34:37 +00:00
v - > cargo_type = new_cid ;
v - > cargo_cap = amount ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
}
2005-07-31 13:08:08 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
_returned_refit_amount = num ;
return cost ;
}
typedef struct TrainFindDepotData {
uint best_length ;
2005-06-24 12:38:35 +00:00
TileIndex tile ;
2005-09-30 20:37:25 +00:00
PlayerID owner ;
2005-05-07 22:00:36 +00:00
/**
2005-11-14 19:48:04 +00:00
* true if reversing is necessary for the train to get to this depot
* This value is unused when new depot finding and NPF are both disabled
2005-05-07 22:00:36 +00:00
*/
bool reverse ;
2004-08-09 17:04:08 +00:00
} TrainFindDepotData ;
2005-07-19 11:42:40 +00:00
static bool NtpCallbFindDepot ( TileIndex tile , TrainFindDepotData * tfdd , int track , uint length )
2004-08-09 17:04:08 +00:00
{
2005-06-04 11:56:32 +00:00
if ( IsTileType ( tile , MP_RAILWAY ) & & IsTileOwner ( tile , tfdd - > owner ) ) {
2005-07-13 18:04:01 +00:00
if ( ( _m [ tile ] . m5 & ~ 0x3 ) = = 0xC0 ) {
2005-07-19 11:42:40 +00:00
tfdd - > best_length = length ;
tfdd - > tile = tile ;
2004-08-09 17:04:08 +00:00
return true ;
}
}
2005-07-19 11:42:40 +00:00
return false ;
2004-08-09 17:04:08 +00:00
}
2005-05-03 20:45:23 +00:00
// returns the tile of a depot to goto to. The given vehicle must not be
// crashed!
2004-09-23 21:20:38 +00:00
static TrainFindDepotData FindClosestTrainDepot ( Vehicle * v )
2004-08-09 17:04:08 +00:00
{
int i ;
TrainFindDepotData tfdd ;
2005-06-24 12:38:35 +00:00
TileIndex tile = v - > tile ;
2004-08-09 17:04:08 +00:00
2005-05-03 20:45:23 +00:00
assert ( ! ( v - > vehstatus & VS_CRASHED ) ) ;
2004-08-09 17:04:08 +00:00
tfdd . owner = v - > owner ;
tfdd . best_length = ( uint ) - 1 ;
2005-05-07 22:00:36 +00:00
tfdd . reverse = false ;
2004-08-09 17:04:08 +00:00
2005-02-06 22:36:08 +00:00
if ( IsTileDepotType ( tile , TRANSPORT_RAIL ) ) {
2004-09-23 21:20:38 +00:00
tfdd . tile = tile ;
tfdd . best_length = 0 ;
return tfdd ;
}
2005-10-23 13:04:44 +00:00
if ( v - > u . rail . track = = 0x40 ) tile = GetVehicleOutOfTunnelTile ( v ) ;
2004-09-23 21:20:38 +00:00
2005-01-31 11:23:10 +00:00
if ( _patches . new_pathfinding_all ) {
NPFFoundTargetData ftd ;
2005-05-07 22:00:36 +00: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 18:04:02 +00:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
Trackdir trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
2005-05-07 22:00:36 +00: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 18:04:02 +00:00
assert ( trackdir ! = INVALID_TRACKDIR ) ;
2005-07-03 13:02:54 +00:00
ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , NPF_INFINITE_PENALTY ) ;
2005-01-31 11:23:10 +00:00
if ( ftd . best_bird_dist = = 0 ) {
/* Found target */
tfdd . tile = ftd . node . tile ;
2005-04-11 12:08:09 +00: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-07 22:00:36 +00:00
tfdd . best_length = ftd . best_path_dist / NPF_TILE_LENGTH ;
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) )
tfdd . reverse = true ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
} else {
// search in the forward direction first.
i = v - > direction > > 1 ;
2005-11-14 19:48:04 +00:00
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) i = ( i - 1 ) & 3 ;
2005-07-19 11:42:40 +00:00
NewTrainPathfind ( tile , 0 , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2004-09-23 21:20:38 +00:00
if ( tfdd . best_length = = ( uint ) - 1 ) {
2005-05-07 22:00:36 +00:00
tfdd . reverse = true ;
2004-09-23 21:20:38 +00:00
// search in backwards direction
i = ( v - > direction ^ 4 ) > > 1 ;
2005-11-14 19:48:04 +00:00
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) i = ( i - 1 ) & 3 ;
2005-07-19 11:42:40 +00:00
NewTrainPathfind ( tile , 0 , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2004-09-23 21:20:38 +00:00
}
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2004-09-23 21:20:38 +00:00
return tfdd ;
2004-08-09 17:04:08 +00:00
}
2005-05-09 22:33:00 +00: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 00:18:30 +00:00
int32 CmdSendTrainToDepot ( int x , int y , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
2005-01-30 20:50:06 +00:00
Vehicle * v ;
2004-09-23 21:20:38 +00:00
TrainFindDepotData tfdd ;
2004-08-09 17:04:08 +00:00
2005-01-30 20:50:06 +00:00
if ( ! IsVehicleIndex ( p1 ) ) return CMD_ERROR ;
v = GetVehicle ( p1 ) ;
2005-05-09 22:33:00 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2005-01-29 12:01:36 +00:00
2005-05-09 22:33:00 +00:00
if ( v - > vehstatus & VS_CRASHED ) return CMD_ERROR ;
2005-05-03 19:31:33 +00:00
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
2005-04-02 01:08:01 +00:00
if ( HASBIT ( v - > current_order . flags , OFB_PART_OF_ORDERS ) ) {
2004-08-09 17:04:08 +00:00
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
}
2004-09-10 19:02:27 +00:00
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
return 0 ;
}
2004-09-23 21:20:38 +00:00
tfdd = FindClosestTrainDepot ( v ) ;
if ( tfdd . best_length = = ( uint ) - 1 )
2004-08-09 17:04:08 +00:00
return_cmd_error ( STR_883A_UNABLE_TO_FIND_ROUTE_TO ) ;
if ( flags & DC_EXEC ) {
2004-09-23 21:20:38 +00:00
v - > dest_tile = tfdd . tile ;
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_GOTO_DEPOT ;
2005-03-19 21:16:22 +00:00
v - > current_order . flags = OF_NON_STOP | OF_FULL_LOAD ;
2005-02-06 10:18:47 +00:00
v - > current_order . station = GetDepotByTile ( tfdd . tile ) - > index ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2005-05-07 22:00:36 +00: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 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
return 0 ;
}
2005-01-22 20:23:18 +00:00
void OnTick_Train ( void )
2004-08-09 17:04:08 +00:00
{
_age_cargo_skip_counter = ( _age_cargo_skip_counter = = 0 ) ? 184 : ( _age_cargo_skip_counter - 1 ) ;
}
2005-11-04 12:58:18 +00:00
static const int8 _vehicle_smoke_pos [ 8 ] = {
1 , 1 , 1 , 0 , - 1 , - 1 , - 1 , 0
2004-08-09 17:04:08 +00:00
} ;
2005-11-13 13:43:55 +00:00
static void HandleLocomotiveSmokeCloud ( const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2005-11-13 13:43:55 +00:00
const Vehicle * u ;
2004-08-09 17:04:08 +00:00
if ( v - > vehstatus & VS_TRAIN_SLOWING | | v - > load_unload_time_rem ! = 0 | | v - > cur_speed < 2 )
return ;
u = v ;
do {
2005-10-01 12:43:34 +00:00
EngineID engtype = v - > engine_type ;
2005-11-04 12:58:18 +00:00
int effect_offset = GB ( v - > u . rail . cached_vis_effect , 0 , 4 ) - 8 ;
byte effect_type = GB ( v - > u . rail . cached_vis_effect , 4 , 2 ) ;
bool disable_effect = HASBIT ( v - > u . rail . cached_vis_effect , 6 ) ;
int x , y ;
2004-08-09 17:04:08 +00:00
// no smoke?
2005-11-04 12:58:18 +00:00
if ( ( RailVehInfo ( engtype ) - > flags & RVI_WAGON & & effect_type = = 0 ) | |
disable_effect | |
2005-10-16 07:58:15 +00:00
GetEngine ( engtype ) - > railtype > RAILTYPE_RAIL | |
2005-11-14 19:48:04 +00:00
v - > vehstatus & VS_HIDDEN | |
v - > u . rail . track & 0xC0 ) {
2004-08-09 17:04:08 +00:00
continue ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
2005-11-07 13:02:33 +00:00
// No smoke in depots or tunnels
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) | | IsTunnelTile ( v - > tile ) )
continue ;
2005-11-04 12:58:18 +00:00
if ( effect_type = = 0 ) {
// Use default effect type for engine class.
effect_type = RailVehInfo ( engtype ) - > engclass ;
} else {
effect_type - - ;
}
x = _vehicle_smoke_pos [ v - > direction ] * effect_offset ;
y = _vehicle_smoke_pos [ ( v - > direction + 2 ) % 8 ] * effect_offset ;
switch ( effect_type ) {
2004-08-09 17:04:08 +00:00
case 0 :
// steam smoke.
2005-11-07 13:02:33 +00:00
if ( GB ( v - > tick_counter , 0 , 4 ) = = 0 ) {
2005-11-04 12:58:18 +00:00
CreateEffectVehicleRel ( v , x , y , 10 , EV_STEAM_SMOKE ) ;
2004-08-09 17:04:08 +00:00
}
break ;
case 1 :
// diesel smoke
2005-11-14 09:21:05 +00:00
if ( u - > cur_speed < = 40 & & CHANCE16 ( 15 , 128 ) ) {
2005-02-12 15:53:32 +00:00
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_DIESEL_SMOKE ) ;
2004-08-09 17:04:08 +00:00
}
break ;
case 2 :
// blue spark
2005-11-14 09:21:05 +00:00
if ( GB ( v - > tick_counter , 0 , 2 ) = = 0 & & CHANCE16 ( 1 , 45 ) ) {
2005-02-12 15:53:32 +00:00
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_ELECTRIC_SPARK ) ;
2004-08-09 17:04:08 +00:00
}
break ;
}
2005-11-14 09:21:05 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
}
2005-10-23 13:04:44 +00:00
static void TrainPlayLeaveStationSound ( const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2004-12-04 09:26:39 +00:00
static const SoundFx sfx [ ] = {
SND_04_TRAIN ,
SND_0A_TRAIN_HORN ,
SND_0A_TRAIN_HORN
} ;
2005-10-01 12:43:34 +00:00
EngineID engtype = v - > engine_type ;
2004-08-09 17:04:08 +00:00
2005-06-07 18:13:49 +00:00
switch ( GetEngine ( engtype ) - > railtype ) {
2005-10-16 07:58:15 +00:00
case RAILTYPE_RAIL :
2004-12-04 07:41:37 +00:00
SndPlayVehicleFx ( sfx [ RailVehInfo ( engtype ) - > engclass ] , v ) ;
2004-11-05 23:12:33 +00:00
break ;
2005-10-16 07:58:15 +00:00
case RAILTYPE_MONO :
2004-11-05 23:12:33 +00:00
SndPlayVehicleFx ( SND_47_MAGLEV_2 , v ) ;
break ;
2005-10-16 07:58:15 +00:00
case RAILTYPE_MAGLEV :
2004-11-05 23:12:33 +00:00
SndPlayVehicleFx ( SND_41_MAGLEV , v ) ;
break ;
2004-08-09 17:04:08 +00:00
}
}
static bool CheckTrainStayInDepot ( Vehicle * v )
{
Vehicle * u ;
2004-11-23 20:25:18 +00:00
// bail out if not all wagons are in the same depot or not in a depot at all
2005-11-14 19:48:04 +00:00
for ( u = v ; u ! = NULL ; u = u - > next ) {
if ( u - > u . rail . track ! = 0x80 | | u - > tile ! = v - > tile ) return false ;
}
2004-08-09 17:04:08 +00:00
if ( v - > u . rail . force_proceed = = 0 ) {
2005-11-13 21:16:34 +00:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
2005-01-24 22:24:47 +00:00
if ( + + v - > load_unload_time_rem < 37 ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
return true ;
2005-01-24 22:24:47 +00:00
}
2004-08-09 17:04:08 +00:00
v - > load_unload_time_rem = 0 ;
2005-07-22 08:40:19 +00:00
if ( PBSIsPbsSegment ( v - > tile , trackdir ) ) {
2005-07-04 14:58:55 +00:00
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
2005-11-14 19:48:04 +00:00
if ( PBSTileUnavail ( v - > tile ) & ( 1 < < trackdir ) ) return true ;
2005-07-04 14:58:55 +00:00
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 ) ) {
2005-11-14 19:48:04 +00:00
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_BLOCKED ) | | NPFGetFlag ( & ftd . node , NPF_FLAG_PBS_RED ) ) {
2005-07-04 14:58:55 +00:00
return true ;
2005-11-14 19:48:04 +00:00
} else {
2005-07-17 20:09:02 +00:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 14:58:55 +00:00
goto green ;
2005-07-17 20:09:02 +00:00
}
2005-07-04 14:58:55 +00:00
}
}
2005-01-24 22:24:47 +00:00
if ( UpdateSignalsOnSegment ( v - > tile , v - > direction ) ) {
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
return true ;
2005-01-24 22:24:47 +00:00
}
2004-08-09 17:04:08 +00:00
}
2005-07-04 14:58:55 +00:00
green :
2004-12-09 21:46:56 +00:00
VehicleServiceInDepot ( v ) ;
2005-01-24 22:24:47 +00:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
TrainPlayLeaveStationSound ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
v - > u . rail . track = 1 ;
2005-11-14 19:48:04 +00:00
if ( v - > direction & 2 ) v - > u . rail . track = 2 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
v - > vehstatus & = ~ VS_HIDDEN ;
v - > cur_speed = 0 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 11:23:10 +00:00
/* Check for station tiles */
2004-08-09 17:04:08 +00:00
typedef struct TrainTrackFollowerData {
TileIndex dest_coords ;
2005-03-25 10:40:58 +00:00
StationID station_index ; // station index we're heading for
2004-08-09 17:04:08 +00:00
uint best_bird_dist ;
uint best_track_dist ;
byte best_track ;
} TrainTrackFollowerData ;
2005-07-19 11:42:40 +00:00
static bool NtpCallbFindStation ( TileIndex tile , TrainTrackFollowerData * ttfd , int track , uint length )
2005-06-24 12:38:35 +00:00
{
2004-08-09 17:04:08 +00:00
// heading for nowhere?
if ( ttfd - > dest_coords = = 0 )
return false ;
// did we reach the final station?
2005-07-12 20:28:19 +00:00
if ( ( ttfd - > station_index = = INVALID_STATION & & tile = = ttfd - > dest_coords ) | |
2005-07-13 18:04:01 +00:00
( IsTileType ( tile , MP_STATION ) & & IS_BYTE_INSIDE ( _m [ tile ] . m5 , 0 , 8 ) & & _m [ tile ] . m2 = = ttfd - > station_index ) ) {
2005-07-12 20:28:19 +00: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 17:04:08 +00:00
// found station
2005-07-19 11:42:40 +00:00
ttfd - > best_track = track ;
2004-08-09 17:04:08 +00:00
return true ;
} else {
uint dist ;
2005-07-19 11:42:40 +00:00
// didn't find station, keep track of the best path so far.
2005-01-31 07:23:15 +00:00
dist = DistanceManhattan ( tile , ttfd - > dest_coords ) ;
2004-08-09 17:04:08 +00:00
if ( dist < ttfd - > best_bird_dist ) {
ttfd - > best_bird_dist = dist ;
2005-07-19 11:42:40 +00:00
ttfd - > best_track = track ;
2004-08-09 17:04:08 +00:00
}
return false ;
}
}
2005-11-13 13:43:55 +00:00
static void FillWithStationData ( TrainTrackFollowerData * fd , const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2005-11-14 19:48:04 +00:00
fd - > dest_coords = v - > dest_tile ;
if ( v - > current_order . type = = OT_GOTO_STATION ) {
fd - > station_index = v - > current_order . station ;
} else {
fd - > station_index = INVALID_STATION ;
}
2004-08-09 17:04:08 +00: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-12-14 09:15:06 +00:00
# ifdef PF_BENCHMARK
2005-07-12 20:28:19 +00:00
# if !defined(_MSC_VER)
2005-10-02 22:39:56 +00:00
unsigned int _rdtsc ( )
2005-01-31 11:23:10 +00:00
{
2005-11-14 19:48:04 +00:00
unsigned int high , low ;
2005-01-31 11:23:10 +00:00
2005-11-14 19:48:04 +00:00
__asm__ __volatile__ ( " rdtsc " : " =a " ( low ) , " =d " ( high ) ) ;
return low ;
2005-01-31 11:23:10 +00:00
}
2005-07-12 20:28:19 +00:00
# else
2005-10-02 22:39:56 +00:00
# ifndef _M_AMD64
static unsigned int _declspec ( naked ) _rdtsc ( void )
2005-07-12 20:28:19 +00:00
{
_asm {
rdtsc
ret
}
}
# endif
2005-01-31 11:23:10 +00:00
# endif
2005-10-02 22:39:56 +00:00
# endif
2005-01-31 11:23:10 +00:00
2004-08-09 17:04:08 +00:00
2005-07-12 20:28:19 +00:00
2004-08-09 17:04:08 +00:00
/* choose a track */
2005-06-24 12:38:35 +00:00
static byte ChooseTrainTrack ( Vehicle * v , TileIndex tile , int enterdir , TrackdirBits trackdirbits )
2004-09-10 19:02:27 +00:00
{
2004-08-09 17:04:08 +00:00
TrainTrackFollowerData fd ;
uint best_track ;
2005-12-14 09:15:06 +00:00
# ifdef PF_BENCHMARK
2005-10-02 22:39:56 +00:00
int time = _rdtsc ( ) ;
2004-08-09 17:04:08 +00:00
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 18:04:02 +00:00
assert ( ( trackdirbits & ~ 0x3F ) = = 0 ) ;
2004-08-09 17:04:08 +00:00
2005-01-31 11:23:10 +00: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 18:04:02 +00:00
if ( KILL_FIRST_BIT ( trackdirbits ) = = 0 )
return FIND_FIRST_BIT ( trackdirbits ) ;
2004-08-09 17:04:08 +00:00
2005-01-31 11:23:10 +00: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 18:04:02 +00:00
Trackdir trackdir ;
2005-07-04 14:58:55 +00:00
uint16 pbs_tracks ;
2005-01-31 11:23:10 +00:00
NPFFillWithOrderData ( & fstd , v ) ;
/* The enterdir for the new tile, is the exitdir for the old tile */
2005-05-02 23:59:11 +00:00
trackdir = GetVehicleTrackdir ( v ) ;
2005-01-31 11:23:10 +00:00
assert ( trackdir ! = 0xff ) ;
2005-07-04 14:58:55 +00: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 20:09:02 +00: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 14:58:55 +00: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 20:09:02 +00:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 14:58:55 +00:00
} else
ftd = NPFRouteToStationOrTile ( tile - TileOffsByDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . railtype , PBS_MODE_NONE ) ;
2005-04-15 13:48:08 +00:00
if ( ftd . best_trackdir = = 0xff ) {
/* We are already at our target. Just do something */
2005-01-31 11:23:10 +00: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 18:04:02 +00:00
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
2004-08-09 17:04:08 +00:00
} else {
2005-04-15 13:48:08 +00: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 11:23:10 +00:00
/* Discard enterdir information, making it a normal track */
2005-07-04 14:58:55 +00:00
best_track = TrackdirToTrack ( ftd . best_trackdir ) ;
2004-08-09 17:04:08 +00:00
}
} else {
2005-01-31 11:23:10 +00:00
FillWithStationData ( & fd , v ) ;
2004-08-09 17:04:08 +00:00
2005-07-12 20:28:19 +00:00
/* New train pathfinding */
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
fd . best_track = 0xFF ;
2004-09-10 19:02:27 +00:00
2005-07-19 11:42:40 +00:00
NewTrainPathfind ( tile - TileOffsByDir ( enterdir ) , v - > dest_tile ,
enterdir , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
2005-01-31 11:23:10 +00:00
2005-07-12 20:28:19 +00:00
if ( fd . best_track = = 0xff ) {
// blaha
best_track = FIND_FIRST_BIT ( trackdirbits ) ;
2005-01-31 11:23:10 +00:00
} else {
2005-07-12 20:28:19 +00:00
best_track = fd . best_track & 7 ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
}
2005-12-14 09:15:06 +00:00
# ifdef PF_BENCHMARK
2005-10-02 22:39:56 +00:00
time = _rdtsc ( ) - time ;
2004-08-09 17:04:08 +00:00
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 11:23:10 +00: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 17:04:08 +00:00
2005-01-31 11:23:10 +00:00
NPFFillWithOrderData ( & fstd , v ) ;
2004-08-09 17:04:08 +00:00
2005-05-02 23:59:11 +00: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 18:04:02 +00:00
trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
2005-01-31 11:23:10 +00:00
assert ( trackdir ! = 0xff ) ;
assert ( trackdir_rev ! = 0xff ) ;
2005-07-04 14:58:55 +00: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 11:23:10 +00: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 20:13:21 +00:00
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) )
2005-01-31 11:23:10 +00: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 11:42:40 +00:00
NewTrainPathfind ( v - > tile , v - > dest_tile , reverse ^ i , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
2005-01-31 11:23:10 +00: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 17:04:08 +00:00
} else {
2005-01-31 11:23:10 +00: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 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2005-01-31 11:23:10 +00:00
/* if we reach this position, there's two paths of equal value so far.
* pick one randomly . */
2005-07-21 06:31:02 +00:00
r = GB ( Random ( ) , 0 , 8 ) ;
2005-01-31 11:23:10 +00: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 17:04:08 +00:00
good : ;
2005-01-31 11:23:10 +00:00
best_track = i ;
best_bird_dist = fd . best_bird_dist ;
best_track_dist = fd . best_track_dist ;
reverse_best = reverse ;
2004-08-09 17:04:08 +00:00
bad : ;
2005-01-31 11:23:10 +00:00
if ( reverse ! = 0 )
break ;
reverse = 2 ;
}
2004-08-09 17:04:08 +00:00
}
return reverse_best ! = 0 ;
}
static bool ProcessTrainOrder ( Vehicle * v )
{
2005-01-15 19:06:22 +00:00
const Order * order ;
2004-08-09 17:04:08 +00:00
bool result ;
// These are un-interruptible
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type > = OT_GOTO_DEPOT & &
v - > current_order . type < = OT_LEAVESTATION ) {
2005-01-15 19:06:22 +00:00
// Let a depot order in the orderlist interrupt.
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type ! = OT_GOTO_DEPOT | |
! ( v - > current_order . flags & OF_UNLOAD ) )
2004-08-09 17:04:08 +00:00
return false ;
}
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
2005-07-28 06:09:15 +00:00
( v - > current_order . flags & ( OF_PART_OF_ORDERS | OF_SERVICE_IF_NEEDED ) ) = = ( OF_PART_OF_ORDERS | OF_SERVICE_IF_NEEDED ) & &
2005-03-19 21:16:22 +00:00
! VehicleNeedsService ( v ) ) {
2004-08-09 17:04:08 +00:00
v - > cur_order_index + + ;
}
2004-11-14 13:07:07 +00:00
// check if we've reached the waypoint?
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_WAYPOINT & & v - > tile = = v - > dest_tile ) {
2004-08-09 17:04:08 +00:00
v - > cur_order_index + + ;
}
2004-09-10 19:02:27 +00:00
// check if we've reached a non-stop station while TTDPatch nonstop is enabled..
2005-10-23 13:04:44 +00:00
if ( _patches . new_nonstop & &
v - > current_order . flags & OF_NON_STOP & &
IsTileType ( v - > tile , MP_STATION ) & &
v - > current_order . station = = _m [ v - > tile ] . m2 ) {
2004-09-10 19:02:27 +00:00
v - > cur_order_index + + ;
2004-08-09 17:04:08 +00:00
}
// Get the current order
2005-11-14 19:48:04 +00:00
if ( v - > cur_order_index > = v - > num_orders ) v - > cur_order_index = 0 ;
2005-01-15 19:06:22 +00:00
order = GetVehicleOrder ( v , v - > cur_order_index ) ;
2004-08-09 17:04:08 +00:00
// If no order, do nothing.
2005-01-15 19:06:22 +00:00
if ( order = = NULL ) {
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
2004-08-09 17:04:08 +00:00
v - > dest_tile = 0 ;
return false ;
}
// If it is unchanged, keep it.
2005-01-15 19:06:22 +00:00
if ( order - > type = = v - > current_order . type & &
order - > flags = = v - > current_order . flags & &
order - > station = = v - > current_order . station )
2004-08-09 17:04:08 +00:00
return false ;
// Otherwise set it, and determine the destination tile.
2005-01-15 19:06:22 +00:00
v - > current_order = * order ;
2004-08-09 17:04:08 +00:00
v - > dest_tile = 0 ;
result = false ;
2005-01-15 19:06:22 +00:00
switch ( order - > type ) {
case OT_GOTO_STATION :
if ( order - > station = = v - > last_station_visited )
2005-02-02 16:16:43 +00:00
v - > last_station_visited = INVALID_STATION ;
2005-01-15 19:06:22 +00:00
v - > dest_tile = GetStation ( order - > station ) - > xy ;
result = CheckReverseTrain ( v ) ;
break ;
case OT_GOTO_DEPOT :
2005-02-06 10:18:47 +00:00
v - > dest_tile = GetDepot ( order - > station ) - > xy ;
2005-01-15 19:06:22 +00:00
result = CheckReverseTrain ( v ) ;
break ;
case OT_GOTO_WAYPOINT :
2005-03-24 17:03:37 +00:00
v - > dest_tile = GetWaypoint ( order - > station ) - > xy ;
2005-01-15 19:06:22 +00:00
result = CheckReverseTrain ( v ) ;
break ;
2004-08-09 17:04:08 +00:00
}
2005-01-15 19:06:22 +00:00
InvalidateVehicleOrder ( v ) ;
2004-08-09 17:04:08 +00: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 ) ;
2005-11-14 19:48:04 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
}
static void HandleTrainLoading ( Vehicle * v , bool mode )
{
2005-11-14 19:48:04 +00:00
if ( v - > current_order . type = = OT_NOTHING ) return ;
2004-09-10 19:02:27 +00:00
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type ! = OT_DUMMY ) {
2005-11-14 19:48:04 +00:00
if ( v - > current_order . type ! = OT_LOADING ) return ;
if ( mode ) return ;
2004-08-09 17:04:08 +00:00
// don't mark the train as lost if we're loading on the final station.
2004-12-05 12:43:04 +00:00
if ( v - > current_order . flags & OF_NON_STOP )
2004-08-09 17:04:08 +00:00
v - > u . rail . days_since_order_progr = 0 ;
2005-11-14 19:48:04 +00:00
if ( - - v - > load_unload_time_rem ) return ;
2004-08-09 17:04:08 +00:00
2004-12-05 12:43:04 +00:00
if ( v - > current_order . flags & OF_FULL_LOAD & & CanFillVehicle ( v ) ) {
2005-01-04 00:54:12 +00:00
v - > u . rail . days_since_order_progr = 0 ; /* Prevent a train lost message for full loading trains */
2004-08-09 17:04:08 +00:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
MarkTrainDirty ( v ) ;
2004-09-10 19:02:27 +00:00
2005-06-05 15:37:00 +00:00
// need to update acceleration and cached values since the goods on the train changed.
TrainCargoChanged ( v ) ;
2004-08-09 17:04:08 +00:00
UpdateTrainAcceleration ( v ) ;
}
return ;
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
TrainPlayLeaveStationSound ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
{
2004-12-05 12:43:04 +00:00
Order b = v - > current_order ;
v - > current_order . type = OT_LEAVESTATION ;
v - > current_order . flags = 0 ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// If this was not the final order, don't remove it from the list.
2005-11-14 19:48:04 +00:00
if ( ! ( b . flags & OF_NON_STOP ) ) return ;
2004-08-09 17:04:08 +00:00
}
}
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
2005-01-15 19:06:22 +00:00
InvalidateVehicleOrder ( v ) ;
2004-08-09 17:04:08 +00:00
}
static int UpdateTrainSpeed ( Vehicle * v )
{
uint spd ;
uint accel ;
2005-01-09 16:02:06 +00:00
if ( v - > vehstatus & VS_STOPPED | | HASBIT ( v - > u . rail . flags , VRF_REVERSING ) ) {
2005-11-14 19:48:04 +00:00
if ( _patches . realistic_acceleration ) {
2005-01-26 12:51:04 +00:00
accel = GetTrainAcceleration ( v , AM_BRAKE ) * 2 ;
2005-11-14 19:48:04 +00:00
} else {
2005-01-26 12:51:04 +00:00
accel = v - > acceleration * - 2 ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
} else {
2005-11-14 19:48:04 +00:00
if ( _patches . realistic_acceleration ) {
2005-01-26 12:51:04 +00:00
accel = GetTrainAcceleration ( v , AM_ACCEL ) ;
2005-11-14 19:48:04 +00:00
} else {
2005-01-26 12:51:04 +00:00
accel = v - > acceleration ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
}
spd = v - > subspeed + accel * 2 ;
v - > subspeed = ( byte ) spd ;
2005-01-26 12:51:04 +00: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 17:04:08 +00:00
if ( ! ( v - > direction & 1 ) ) spd = spd * 3 > > 2 ;
spd + = v - > progress ;
v - > progress = ( byte ) spd ;
return ( spd > > 8 ) ;
}
2005-03-25 10:40:58 +00:00
static void TrainEnterStation ( Vehicle * v , StationID station )
2004-08-09 17:04:08 +00:00
{
Station * st ;
uint32 flags ;
v - > last_station_visited = station ;
/* check if a train ever visited this station before */
2005-01-06 22:31:58 +00:00
st = GetStation ( station ) ;
2004-08-09 17:04:08 +00:00
if ( ! ( st - > had_vehicle_of_type & HVOT_TRAIN ) ) {
st - > had_vehicle_of_type | = HVOT_TRAIN ;
2004-12-02 22:53:07 +00:00
SetDParam ( 0 , st - > index ) ;
2004-08-09 17:04:08 +00: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 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_STATION & &
2005-03-25 11:03:05 +00:00
v - > current_order . station = = station ) {
2004-08-09 17:04:08 +00:00
// Yeah, keep the load/unload flags
// Non Stop now means if the order should be increased.
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_LOADING ;
2005-06-15 16:58:15 +00:00
v - > current_order . flags & = OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER ;
2004-12-05 12:43:04 +00:00
v - > current_order . flags | = OF_NON_STOP ;
2004-08-09 17:04:08 +00:00
} else {
// No, just do a simple load
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_LOADING ;
v - > current_order . flags = 0 ;
2004-08-09 17:04:08 +00:00
}
2004-12-05 12:43:04 +00:00
v - > current_order . station = 0 ;
2004-08-09 17:04:08 +00:00
SET_EXPENSES_TYPE ( EXPENSES_TRAIN_INC ) ;
if ( LoadUnloadVehicle ( v ) ! = 0 ) {
InvalidateWindow ( WC_TRAINS_LIST , v - > owner ) ;
2005-06-05 15:37:00 +00:00
TrainCargoChanged ( v ) ;
2004-08-09 17:04:08 +00:00
UpdateTrainAcceleration ( v ) ;
}
2006-01-06 22:49:00 +00:00
MarkTrainDirty ( v ) ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
2005-01-09 16:02:06 +00:00
static byte AfterSetTrainPos ( Vehicle * v , bool new_tile )
2004-08-09 17:04:08 +00:00
{
byte new_z , old_z ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 16:02:06 +00:00
if ( new_tile ) {
CLRBIT ( v - > u . rail . flags , VRF_GOINGUP ) ;
CLRBIT ( v - > u . rail . flags , VRF_GOINGDOWN ) ;
2004-08-09 17:04:08 +00:00
2005-01-09 16:02:06 +00:00
if ( new_z ! = old_z ) {
2005-06-25 06:15:43 +00:00
TileIndex tile = TileVirtXY ( v - > x_pos , v - > y_pos ) ;
2005-04-12 09:17:51 +00:00
// XXX workaround, whole UP/DOWN detection needs overhaul
2005-07-13 18:04:01 +00:00
if ( ! IsTileType ( tile , MP_TUNNELBRIDGE ) | | ( _m [ tile ] . m5 & 0x80 ) ! = 0 )
2005-04-12 09:17:51 +00:00
SETBIT ( v - > u . rail . flags , ( new_z > old_z ) ? VRF_GOINGUP : VRF_GOINGDOWN ) ;
2005-01-09 16:02:06 +00:00
}
2004-08-09 17:04:08 +00: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 12:38:35 +00:00
static int GetNewVehicleDirectionByTile ( TileIndex new_tile , TileIndex old_tile )
2004-08-09 17:04:08 +00:00
{
2005-01-07 17:02:43 +00:00
uint offs = ( TileY ( new_tile ) - TileY ( old_tile ) + 1 ) * 4 +
TileX ( new_tile ) - TileX ( old_tile ) + 1 ;
2004-08-09 17:04:08 +00:00
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
2005-09-18 20:56:44 +00:00
static int GetNewVehicleDirection ( const Vehicle * v , int x , int y )
2004-08-09 17:04:08 +00:00
{
uint offs = ( y - v - > y_pos + 1 ) * 4 + ( x - v - > x_pos + 1 ) ;
assert ( offs < 11 ) ;
return _new_vehicle_direction_table [ offs ] ;
}
2005-09-18 20:56:44 +00:00
static int GetDirectionToVehicle ( const Vehicle * v , int x , int y )
2004-08-09 17:04:08 +00:00
{
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 09:41:46 +00:00
static bool CheckCompatibleRail ( const Vehicle * v , TileIndex tile )
2004-08-09 17:04:08 +00:00
{
2005-01-29 15:12:40 +00:00
switch ( GetTileType ( tile ) ) {
2005-01-17 09:41:46 +00:00
case MP_RAILWAY :
case MP_STATION :
// normal tracks, jump to owner check
break ;
2004-09-21 21:28:42 +00:00
2005-01-17 09:41:46 +00:00
case MP_TUNNELBRIDGE :
2005-07-13 18:04:01 +00:00
if ( ( _m [ tile ] . m5 & 0xC0 ) = = 0xC0 ) { // is bridge middle part?
2005-01-27 12:52:20 +00:00
uint height ;
uint tileh = GetTileSlope ( tile , & height ) ;
2004-08-09 17:04:08 +00:00
2005-01-17 09:41:46 +00:00
// correct Z position of a train going under a bridge on slopes
2005-02-22 12:48:03 +00:00
if ( CorrectZ ( tileh ) ) height + = 8 ;
2005-01-17 09:41:46 +00:00
2005-01-27 12:52:20 +00:00
if ( v - > z_pos ! = height ) return true ; // train is going over bridge
2005-01-17 09:41:46 +00:00
}
break ;
case MP_STREET :
2005-08-23 18:47:04 +00:00
// tracks over roads, do owner check of tracks
2005-01-17 09:41:46 +00:00
return
2005-10-13 14:44:03 +00:00
IsTileOwner ( tile , v - > owner ) & & (
2005-11-18 23:41:03 +00:00
! IsFrontEngine ( v ) | |
2005-10-13 14:44:03 +00:00
IsCompatibleRail ( v - > u . rail . railtype , GB ( _m [ tile ] . m4 , 0 , 4 ) )
) ;
2005-01-17 09:41:46 +00:00
default :
return true ;
}
2004-09-10 19:02:27 +00:00
2005-01-17 09:41:46 +00:00
return
2005-10-23 13:04:44 +00:00
IsTileOwner ( tile , v - > owner ) & & (
2005-11-18 23:41:03 +00:00
! IsFrontEngine ( v ) | |
2005-10-23 13:04:44 +00:00
IsCompatibleRail ( v - > u . rail . railtype , GetRailType ( tile ) )
) ;
2004-08-09 17:04:08 +00: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 ;
2005-11-14 19:48:04 +00:00
if ( old_z = = v - > z_pos | | _patches . realistic_acceleration ) return ;
2004-08-09 17:04:08 +00:00
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 ;
2005-11-14 19:48:04 +00:00
if ( spd < = v - > max_speed ) v - > cur_speed = spd ;
2004-08-09 17:04:08 +00:00
}
}
static const byte _otherside_signal_directions [ 14 ] = {
1 , 3 , 1 , 3 , 5 , 3 , 0 , 0 ,
5 , 7 , 7 , 5 , 7 , 1 ,
} ;
2005-06-24 12:38:35 +00:00
static void TrainMovedChangeSignals ( TileIndex tile , int dir )
2004-08-09 17:04:08 +00:00
{
2005-07-13 18:04:01 +00:00
if ( IsTileType ( tile , MP_RAILWAY ) & & ( _m [ tile ] . m5 & 0xC0 ) = = 0x40 ) {
2005-11-14 19:48:04 +00:00
uint i = FindFirstBit2x64 ( ( _m [ tile ] . m5 + ( _m [ tile ] . m5 < < 8 ) ) & _reachable_tracks [ dir ] ) ;
2004-08-09 17:04:08 +00:00
UpdateSignalsOnSegment ( tile , _otherside_signal_directions [ i ] ) ;
}
}
typedef struct TrainCollideChecker {
2005-03-06 12:54:19 +00:00
const Vehicle * v ;
const Vehicle * v_skip ;
2004-08-09 17:04:08 +00:00
} TrainCollideChecker ;
2005-03-06 12:54:19 +00:00
static void * FindTrainCollideEnum ( Vehicle * v , void * data )
2004-08-09 17:04:08 +00:00
{
2005-03-06 12:54:19 +00: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 17:04:08 +00: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 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
u = v ;
BEGIN_ENUM_WAGONS ( v )
v - > vehstatus | = VS_CRASHED ;
END_ENUM_WAGONS ( v )
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , u - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
2005-10-23 13:04:44 +00:00
static uint CountPassengersInTrain ( const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2005-10-23 13:04:44 +00:00
uint num = 0 ;
2004-08-09 17:04:08 +00:00
BEGIN_ENUM_WAGONS ( v )
2005-01-19 19:15:03 +00:00
if ( v - > cargo_type = = CT_PASSENGERS ) num + = v - > cargo_count ;
2004-08-09 17:04:08 +00:00
END_ENUM_WAGONS ( v )
return num ;
}
2004-08-11 22:07:08 +00:00
/*
2005-03-06 12:54:19 +00:00
* Checks whether the specified train has a collision with another vehicle . If
2005-11-18 23:41:03 +00:00
* so , destroys this vehicle , and the other vehicle if its subtype has TS_Front .
2004-08-11 22:07:08 +00:00
* Reports the incident in a flashy news item , modifies station ratings and
* plays a sound .
*/
2004-08-09 17:04:08 +00:00
static void CheckTrainCollision ( Vehicle * v )
{
TrainCollideChecker tcc ;
2005-03-06 12:54:19 +00:00
Vehicle * coll ;
Vehicle * realcoll ;
2005-11-14 19:48:04 +00:00
uint num ;
2004-08-09 17:04:08 +00:00
/* can't collide in depot */
2005-11-14 19:48:04 +00:00
if ( v - > u . rail . track = = 0x80 ) return ;
2004-09-10 19:02:27 +00:00
2005-06-25 06:15:43 +00:00
assert ( v - > u . rail . track = = 0x40 | | TileVirtXY ( v - > x_pos , v - > y_pos ) = = v - > tile ) ;
2004-08-09 17:04:08 +00:00
tcc . v = v ;
tcc . v_skip = v - > next ;
/* find colliding vehicle */
2005-06-25 06:15:43 +00:00
realcoll = VehicleFromPos ( TileVirtXY ( v - > x_pos , v - > y_pos ) , & tcc , FindTrainCollideEnum ) ;
2005-11-14 19:48:04 +00:00
if ( realcoll = = NULL ) return ;
2004-09-10 19:02:27 +00:00
2005-03-06 12:54:19 +00:00
coll = GetFirstVehicleInChain ( realcoll ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* it can't collide with its own wagons */
2005-03-06 12:54:19 +00:00
if ( v = = coll | |
( v - > u . rail . track & 0x40 & & ( v - > direction & 2 ) ! = ( realcoll - > direction & 2 ) ) )
2004-08-09 17:04:08 +00:00
return ;
2004-09-10 19:02:27 +00:00
//two drivers + passangers killed in train v
2004-08-09 17:04:08 +00:00
num = 2 + CountPassengersInTrain ( v ) ;
2005-03-06 12:54:19 +00:00
if ( ! ( coll - > vehstatus & VS_CRASHED ) )
2004-08-09 17:04:08 +00:00
//two drivers + passangers killed in train coll (if it was not crashed already)
num + = 2 + CountPassengersInTrain ( coll ) ;
SetVehicleCrashed ( v ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( coll ) ) SetVehicleCrashed ( coll ) ;
2004-09-10 19:02:27 +00:00
2004-12-02 22:53:07 +00:00
SetDParam ( 0 , num ) ;
2004-08-09 17:04:08 +00:00
AddNewsItem ( STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL ,
2005-03-06 12:54:19 +00:00
NEWS_FLAGS ( NM_THIN , NF_VIEWPORT | NF_VEHICLE , NT_ACCIDENT , 0 ) ,
2004-08-09 17:04:08 +00:00
v - > index ,
2005-03-06 12:54:19 +00:00
0
) ;
2004-08-09 17:04:08 +00:00
ModifyStationRatingAround ( v - > tile , v - > owner , - 160 , 30 ) ;
2004-12-04 09:26:39 +00:00
SndPlayVehicleFx ( SND_13_BIG_CRASH , v ) ;
2004-08-09 17:04:08 +00:00
}
2005-03-06 12:41:18 +00:00
typedef struct VehicleAtSignalData {
TileIndex tile ;
byte direction ;
} VehicleAtSignalData ;
2004-08-09 17:04:08 +00:00
static void * CheckVehicleAtSignal ( Vehicle * v , void * data )
{
2005-03-06 12:41:18 +00:00
const VehicleAtSignalData * vasd = data ;
2005-11-18 23:41:03 +00:00
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) & &
2005-03-06 12:41:18 +00:00
v - > tile = = vasd - > tile ) {
byte diff = ( v - > direction - vasd - > direction + 2 ) & 7 ;
2004-08-09 17:04:08 +00:00
if ( diff = = 2 | | ( v - > cur_speed < = 5 & & diff < = 4 ) )
2005-03-06 12:41:18 +00:00
return v ;
2004-08-09 17:04:08 +00:00
}
2005-03-06 12:41:18 +00:00
return NULL ;
2004-08-09 17:04:08 +00:00
}
static void TrainController ( Vehicle * v )
{
2005-06-21 14:50:08 +00:00
Vehicle * prev ;
2004-08-09 17:04:08 +00:00
GetNewVehiclePosResult gp ;
uint32 r , tracks , ts ;
2005-01-31 11:23:10 +00:00
int i , enterdir , newdir , dir ;
2004-08-09 17:04:08 +00:00
byte chosen_dir ;
byte chosen_track ;
byte old_z ;
2004-08-11 22:07:08 +00:00
/* For every vehicle after and including the given vehicle */
2005-06-21 14:50:08 +00:00
for ( prev = GetPrevVehicleInChain ( v ) ; v ! = NULL ; prev = v , v = v - > next ) {
2004-08-09 17:04:08 +00:00
BeginVehicleMove ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
if ( v - > u . rail . track ! = 0x40 ) {
2004-08-11 22:07:08 +00:00
/* Not inside tunnel */
2004-08-09 17:04:08 +00:00
if ( GetNewVehiclePos ( v , & gp ) ) {
2004-08-11 22:07:08 +00:00
/* Staying in the old tile */
2004-08-09 17:04:08 +00:00
if ( v - > u . rail . track = = 0x80 ) {
/* inside depot */
gp . x = v - > x_pos ;
gp . y = v - > y_pos ;
} else {
2004-08-11 22:07:08 +00:00
/* is not inside depot */
2004-12-21 16:00:14 +00:00
2005-07-04 14:58:55 +00:00
if ( ( prev = = NULL ) & & ( ! TrainCheckIfLineEnds ( v ) ) )
2004-12-21 16:00:14 +00:00
return ;
2004-08-09 17:04:08 +00:00
r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
2005-01-31 11:23:10 +00:00
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
2004-08-09 17:04:08 +00:00
goto invalid_rail ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
if ( r & 0x2 ) {
TrainEnterStation ( v , r > > 8 ) ;
return ;
}
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_LEAVESTATION ) {
v - > current_order . type = OT_NOTHING ;
v - > current_order . flags = 0 ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
}
} else {
/* A new tile is about to be entered. */
2005-01-31 11:23:10 +00:00
byte bits ;
2004-08-09 17:04:08 +00:00
/* Determine what direction we're entering the new tile from */
dir = GetNewVehicleDirectionByTile ( gp . new_tile , gp . old_tile ) ;
2005-01-31 11:23:10 +00:00
enterdir = dir > > 1 ;
assert ( enterdir = = 0 | | enterdir = = 1 | | enterdir = = 2 | | enterdir = = 3 ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* Get the status of the tracks in the new tile and mask
* away the bits that aren ' t reachable . */
2005-01-31 11:23:10 +00:00
ts = GetTileTrackStatus ( gp . new_tile , TRANSPORT_RAIL ) & _reachable_tracks [ enterdir ] ;
2004-08-09 17:04:08 +00: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 11:23:10 +00: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 18:04:02 +00:00
bits & = ~ TrackCrossesTracks ( FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2005-01-31 11:23:10 +00:00
if ( bits = = 0 ) {
//debug("%x == 0", bits);
2004-08-09 17:04:08 +00:00
goto invalid_rail ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
/* Check if the new tile contrains tracks that are compatible
* with the current train , if not , bail out . */
2005-01-31 11:23:10 +00:00
if ( ! CheckCompatibleRail ( v , gp . new_tile ) ) {
//debug("!CheckCompatibleRail(%p, %x)", v, gp.new_tile);
2004-08-09 17:04:08 +00:00
goto invalid_rail ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
if ( prev = = NULL ) {
2005-07-04 14:58:55 +00:00
byte trackdir ;
2004-08-09 17:04:08 +00:00
/* Currently the locomotive is active. Determine which one of the
* available tracks to choose */
2005-01-31 11:23:10 +00:00
chosen_track = 1 < < ChooseTrainTrack ( v , gp . new_tile , enterdir , bits ) ;
assert ( chosen_track & tracks ) ;
2004-08-09 17:04:08 +00:00
2005-07-04 14:58:55 +00:00
trackdir = TrackEnterdirToTrackdir ( FIND_FIRST_BIT ( chosen_track ) , enterdir ) ;
assert ( trackdir ! = 0xff ) ;
2005-09-23 17:37:21 +00:00
if ( PBSIsPbsSignal ( gp . new_tile , trackdir ) & & PBSIsPbsSegment ( gp . new_tile , trackdir ) ) {
2005-07-04 14:58:55 +00:00
// 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 20:09:02 +00:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 14:58:55 +00: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 17:04:08 +00: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 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* The wagon is active, simply follow the prev vehicle. */
2005-01-31 11:23:10 +00:00
chosen_track = ( byte ) ( _matching_tracks [ GetDirectionToVehicle ( prev , gp . x , gp . y ) ] & bits ) ;
2004-08-09 17:04:08 +00:00
}
2005-07-04 14:58:55 +00:00
green_light :
if ( v - > next = = NULL )
PBSClearTrack ( gp . old_tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2004-08-09 17:04:08 +00: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 11:23:10 +00:00
const byte * b = _initial_tile_subcoord [ FIND_FIRST_BIT ( chosen_track ) ] [ enterdir ] ;
2004-08-09 17:04:08 +00:00
gp . x = ( gp . x & ~ 0xF ) | b [ 0 ] ;
gp . y = ( gp . y & ~ 0xF ) | b [ 1 ] ;
chosen_dir = b [ 2 ] ;
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 11:23:10 +00:00
if ( r & 0x8 ) {
//debug("%x & 0x8", r);
2004-08-09 17:04:08 +00:00
goto invalid_rail ;
2005-01-31 11:23:10 +00:00
}
2004-08-09 17:04:08 +00:00
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( v ) ) v - > load_unload_time_rem = 0 ;
2004-08-09 17:04:08 +00:00
if ( ! ( r & 0x4 ) ) {
v - > tile = gp . new_tile ;
v - > u . rail . track = chosen_track ;
2005-02-06 22:36:08 +00:00
assert ( v - > u . rail . track ) ;
2004-08-09 17:04:08 +00:00
}
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( v ) )
2005-07-04 14:58:55 +00:00
TrainMovedChangeSignals ( gp . new_tile , enterdir ) ;
2004-08-09 17:04:08 +00:00
2004-08-11 22:07:08 +00:00
/* Signals can only change when the first
* ( above ) or the last vehicle moves . */
2004-08-09 17:04:08 +00:00
if ( v - > next = = NULL )
2005-07-04 14:58:55 +00:00
TrainMovedChangeSignals ( gp . old_tile , ( enterdir ) ^ 2 ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
if ( prev = = NULL ) {
AffectSpeedByDirChange ( v , chosen_dir ) ;
}
v - > direction = chosen_dir ;
}
} else {
/* in tunnel */
GetNewVehiclePos ( v , & gp ) ;
2004-09-10 19:02:27 +00:00
2005-07-19 11:42:40 +00: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 17:04:08 +00:00
}
}
/* update image of train, as well as delta XY */
2005-01-31 11:23:10 +00:00
newdir = GetNewVehicleDirection ( v , gp . x , gp . y ) ;
UpdateTrainDeltaXY ( v , newdir ) ;
v - > cur_image = GetTrainImage ( v , newdir ) ;
2004-08-09 17:04:08 +00:00
v - > x_pos = gp . x ;
v - > y_pos = gp . y ;
/* update the Z position of the vehicle */
2005-01-09 16:02:06 +00:00
old_z = AfterSetTrainPos ( v , ( gp . new_tile ! = gp . old_tile ) ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
if ( prev = = NULL ) {
2004-08-11 22:07:08 +00:00
/* This is the first vehicle in the train */
2004-08-09 17:04:08 +00:00
AffectSpeedByZChange ( v , old_z ) ;
}
}
2005-03-06 15:15:27 +00:00
return ;
2004-08-09 17:04:08 +00:00
invalid_rail :
2004-08-11 22:07:08 +00:00
/* We've reached end of line?? */
2005-11-14 19:48:04 +00:00
if ( prev ! = NULL ) error ( " !Disconnecting train " ) ;
2004-08-09 17:04:08 +00:00
goto reverse_train_direction ;
red_light : {
2004-08-11 22:07:08 +00:00
/* We're in front of a red signal ?? */
2004-08-09 17:04:08 +00: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 19:02:27 +00:00
2005-07-13 18:04:01 +00:00
if ( ! ( _m [ gp . new_tile ] . m3 & SignalAgainstTrackdir ( i ) ) ) {
2004-08-09 17:04:08 +00: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 18:04:01 +00:00
} else if ( _m [ gp . new_tile ] . m3 & SignalAlongTrackdir ( i ) ) {
2004-08-09 17:04:08 +00: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 12:41:18 +00:00
TileIndex o_tile = gp . new_tile + TileOffsByDir ( enterdir ) ;
2005-03-15 12:21:59 +00:00
VehicleAtSignalData vasd ;
vasd . tile = o_tile ;
vasd . direction = dir ^ 4 ;
2005-03-06 12:41:18 +00:00
2004-08-09 17:04:08 +00:00
/* check if a train is waiting on the other side */
2005-03-06 12:41:18 +00:00
if ( VehicleFromPos ( o_tile , & vasd , CheckVehicleAtSignal ) = = NULL )
2004-08-09 17:04:08 +00:00
return ;
}
}
}
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
reverse_train_direction :
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
ReverseTrainDirection ( v ) ;
}
2005-03-06 12:31:07 +00:00
extern TileIndex CheckTunnelBusy ( TileIndex tile , uint * length ) ;
2004-08-21 09:57:02 +00:00
2005-03-03 23:26:35 +00: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 17:04:08 +00:00
static void DeleteLastWagon ( Vehicle * v )
{
Vehicle * u = v ;
2005-03-03 23:26:35 +00: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 12:54:19 +00:00
for ( ; v - > next ! = NULL ; v = v - > next ) u = v ;
2004-08-09 17:04:08 +00:00
u - > next = NULL ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
DeleteWindowById ( WC_VEHICLE_VIEW , v - > index ) ;
2004-12-10 18:16:08 +00:00
RebuildVehicleLists ( ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
BeginVehicleMove ( v ) ;
EndVehicleMove ( v ) ;
DeleteVehicle ( v ) ;
2005-07-04 14:58:55 +00:00
// clear up reserved pbs tracks
if ( PBSTileReserved ( v - > tile ) & v - > u . rail . track ) {
if ( v = = u ) {
2005-07-17 20:09:02 +00: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 14:58:55 +00:00
} ;
if ( v - > tile ! = u - > tile ) {
PBSClearTrack ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
} ;
}
2005-03-03 23:26:35 +00:00
if ( ! ( v - > u . rail . track & 0xC0 ) )
SetSignalsOnBothDir ( v - > tile , FIND_FIRST_BIT ( v - > u . rail . track ) ) ;
2004-09-10 19:02:27 +00:00
2005-03-06 12:54:19 +00:00
/* Check if the wagon was on a road/rail-crossing and disable it if no
* others are on it */
2005-01-23 10:40:54 +00:00
DisableTrainCrossing ( v - > tile ) ;
2005-03-03 23:26:35 +00:00
if ( v - > u . rail . track = = 0x40 ) { // inside a tunnel
2005-03-06 12:31:07 +00:00
TileIndex endtile = CheckTunnelBusy ( v - > tile , NULL ) ;
2005-03-03 23:26:35 +00:00
2005-03-06 12:31:07 +00:00
if ( endtile = = INVALID_TILE ) // tunnel is busy (error returned)
2005-03-03 23:26:35 +00:00
return ;
2005-03-06 12:33:33 +00: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 09:57:02 +00:00
}
2004-08-09 17:04:08 +00:00
}
static void ChangeTrainDirRandomly ( Vehicle * v )
{
static int8 _random_dir_change [ 4 ] = { - 1 , 0 , 0 , 1 } ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
do {
2004-08-21 09:57:02 +00:00
//I need to buffer the train direction
2004-09-18 16:40:06 +00:00
if ( ! ( v - > u . rail . track & 0x40 ) )
2005-11-14 08:09:57 +00:00
v - > direction = ( v - > direction + _random_dir_change [ GB ( Random ( ) , 0 , 2 ) ] ) & 7 ;
2004-08-09 17:04:08 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
BeginVehicleMove ( v ) ;
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
2005-01-09 16:02:06 +00:00
AfterSetTrainPos ( v , false ) ;
2004-08-09 17:04:08 +00: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 19:02:27 +00:00
2005-11-14 19:48:04 +00:00
if ( state = = 4 & & v - > u . rail . track ! = 0x40 ) {
2005-02-12 15:53:32 +00:00
CreateEffectVehicleRel ( v , 4 , 4 , 8 , EV_EXPLOSION_LARGE ) ;
2004-08-09 17:04:08 +00:00
}
2005-11-14 09:21:05 +00:00
if ( state < = 200 & & CHANCE16R ( 1 , 7 , r ) ) {
2004-08-09 17:04:08 +00:00
index = ( r * 10 > > 16 ) ;
u = v ;
do {
if ( - - index < 0 ) {
2005-01-24 14:39:22 +00:00
r = Random ( ) ;
2004-08-09 17:04:08 +00:00
CreateEffectVehicleRel ( u ,
2005-07-20 15:29:28 +00:00
GB ( r , 8 , 3 ) + 2 ,
GB ( r , 16 , 3 ) + 2 ,
GB ( r , 0 , 3 ) + 5 ,
2005-02-12 15:53:32 +00:00
EV_EXPLOSION_SMALL ) ;
2004-08-09 17:04:08 +00:00
break ;
}
} while ( ( u = u - > next ) ! = NULL ) ;
}
if ( state < = 240 & & ! ( v - > tick_counter & 3 ) ) {
ChangeTrainDirRandomly ( v ) ;
}
2005-01-23 22:01:51 +00:00
if ( state > = 4440 & & ! ( v - > tick_counter & 0x1F ) ) {
2004-08-09 17:04:08 +00:00
DeleteLastWagon ( v ) ;
2005-01-23 22:01:51 +00:00
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ;
}
2004-08-09 17:04:08 +00: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 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
2004-09-10 19:02:27 +00:00
2004-12-04 09:26:39 +00:00
SndPlayVehicleFx ( ( _opt . landscape ! = LT_CANDY ) ?
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2 , v ) ;
2004-08-09 17:04:08 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
Vehicle * u = CreateEffectVehicleRel ( v , 4 , 4 , 5 , EV_BREAKDOWN_SMOKE ) ;
2005-10-23 13:04:44 +00:00
if ( u ! = NULL ) u - > u . special . unk0 = v - > breakdown_delay * 2 ;
2004-08-09 17:04:08 +00:00
}
}
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 16:00:14 +00:00
static bool TrainCheckIfLineEnds ( Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2005-06-24 12:38:35 +00:00
TileIndex tile ;
2004-08-09 17:04:08 +00:00
uint x , y ;
2005-10-03 21:20:01 +00:00
uint16 break_speed ;
2004-08-09 17:04:08 +00:00
int t ;
uint32 ts ;
2005-07-04 14:58:55 +00:00
byte trackdir ;
2004-08-09 17:04:08 +00:00
2005-10-03 21:20:01 +00:00
t = v - > breakdown_ctr ;
if ( t > 1 ) {
2004-08-09 17:04:08 +00:00
v - > vehstatus | = VS_TRAIN_SLOWING ;
2004-09-10 19:02:27 +00:00
2005-10-03 21:20:01 +00:00
break_speed = _breakdown_speeds [ GB ( ~ t , 4 , 4 ) ] ;
if ( break_speed < v - > cur_speed ) v - > cur_speed = break_speed ;
2004-08-09 17:04:08 +00:00
} else {
v - > vehstatus & = ~ VS_TRAIN_SLOWING ;
}
2005-11-14 19:48:04 +00:00
if ( v - > u . rail . track & 0x40 ) return true ; // exit if inside a tunnel
if ( v - > u . rail . track & 0x80 ) return true ; // exit if inside a depot
2005-07-04 14:58:55 +00:00
2004-08-09 17:04:08 +00:00
tile = v - > tile ;
// tunnel entrance?
2005-07-21 06:31:02 +00:00
if ( IsTunnelTile ( tile ) & & GB ( _m [ tile ] . m5 , 0 , 2 ) * 2 + 1 = = v - > direction )
return true ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// depot?
2004-12-21 16:00:14 +00:00
/* XXX -- When enabled, this makes it possible to crash trains of others
( by building a depot right against a station ) */
2005-07-13 18:04:01 +00:00
/* if (IsTileType(tile, MP_RAILWAY) && (_m[tile].m5 & 0xFC) == 0xC0)
2004-12-21 16:00:14 +00:00
return true ; */
2004-08-09 17:04:08 +00:00
2004-08-11 22:07:08 +00:00
/* Determine the non-diagonal direction in which we will exit this tile */
2004-08-09 17:04:08 +00:00
t = v - > direction > > 1 ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ t ] ) {
t = ( t - 1 ) & 3 ;
}
2004-08-11 22:07:08 +00:00
/* Calculate next tile */
2005-01-05 13:32:03 +00:00
tile + = TileOffsByDir ( t ) ;
2004-08-11 22:07:08 +00:00
// determine the track status on the next tile.
2005-01-24 22:24:47 +00:00
ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL ) & _reachable_tracks [ t ] ;
2004-09-10 19:02:27 +00:00
2005-07-04 14:58:55 +00: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-11 22:07:08 +00:00
/* Calc position within the current tile ?? */
2004-08-09 17:04:08 +00:00
x = v - > x_pos & 0xF ;
y = v - > y_pos & 0xF ;
2004-09-10 19:02:27 +00:00
2005-11-14 19:48:04 +00:00
switch ( v - > direction ) {
2004-08-09 17:04:08 +00:00
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 ;
}
2005-10-03 21:20:01 +00:00
if ( GB ( ts , 0 , 16 ) ! = 0 ) {
2004-12-21 16:00:14 +00: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 17:04:08 +00:00
if ( ( ts & = ( ts > > 16 ) ) = = 0 ) {
// make a rail/road crossing red
2005-06-07 21:37:00 +00:00
if ( IsTileType ( tile , MP_STREET ) & & IsLevelCrossing ( tile ) ) {
2005-10-05 07:20:26 +00:00
if ( GB ( _m [ tile ] . m5 , 2 , 1 ) = = 0 ) {
SB ( _m [ tile ] . m5 , 2 , 1 , 1 ) ;
2004-12-04 09:26:39 +00:00
SndPlayVehicleFx ( SND_0E_LEVEL_CROSSING , v ) ;
2004-08-09 17:04:08 +00:00
MarkTileDirtyByTile ( tile ) ;
}
}
2004-12-21 16:00:14 +00:00
return true ;
2004-08-09 17:04:08 +00:00
}
} else if ( x + 4 > 15 ) {
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
2004-12-21 16:00:14 +00:00
return false ;
2004-08-09 17:04:08 +00:00
}
2005-07-04 14:58:55 +00:00
if ( v - > u . rail . pbs_status = = PBS_STAT_HAS_PATH )
return true ;
2005-09-23 17:37:21 +00:00
if ( ( trackdir ! = INVALID_TRACKDIR ) & & ( PBSIsPbsSignal ( tile , trackdir ) & & PBSIsPbsSegment ( tile , trackdir ) ) & & ! ( IsTileType ( v - > tile , MP_STATION ) & & ( v - > current_order . station = = _m [ v - > tile ] . m2 ) ) ) {
2005-07-04 14:58:55 +00: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 20:09:02 +00:00
v - > u . rail . pbs_end_tile = ftd . node . tile ;
v - > u . rail . pbs_end_trackdir = ftd . node . direction ;
2005-07-04 14:58:55 +00:00
return true ;
}
} ;
} ;
2004-08-09 17:04:08 +00:00
// slow down
v - > vehstatus | = VS_TRAIN_SLOWING ;
2005-10-03 21:20:01 +00:00
break_speed = _breakdown_speeds [ x & 0xF ] ;
if ( ! ( v - > direction & 1 ) ) break_speed > > = 1 ;
if ( break_speed < v - > cur_speed ) v - > cur_speed = break_speed ;
2004-12-21 16:00:14 +00:00
return true ;
2004-08-09 17:04:08 +00: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 16:02:06 +00:00
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSING ) & & v - > cur_speed = = 0 ) {
2004-08-09 17:04:08 +00: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 12:43:04 +00:00
if ( v - > current_order . type = = OT_LOADING )
2004-08-09 17:04:08 +00: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-06 22:44:11 +00:00
CheckTrainCollision ( v ) ;
2004-08-09 17:04:08 +00: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-11-18 23:41:03 +00:00
if ( IsFrontEngine ( v ) ) {
2004-08-09 17:04:08 +00:00
TrainLocoHandler ( v , false ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// make sure vehicle wasn't deleted.
2005-11-18 23:41:03 +00:00
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) )
2004-08-09 17:04:08 +00:00
TrainLocoHandler ( v , true ) ;
2005-11-18 23:41:03 +00:00
} else if ( IsFreeWagon ( v ) & & HASBITS ( v - > vehstatus , VS_CRASHED ) ) {
2005-01-24 00:36:22 +00:00
// Delete flooded standalone wagon
if ( + + v - > u . rail . crash_anim_pos > = 4400 )
DeleteVehicle ( v ) ;
2004-08-09 17:04:08 +00:00
}
}
static const byte _depot_track_ind [ 4 ] = { 0 , 1 , 0 , 1 } ;
2004-12-19 09:39:19 +00:00
// Validation for the news item "Train is waiting in depot"
2005-01-22 22:47:58 +00:00
static bool ValidateTrainInDepot ( uint data_a , uint data_b )
2004-12-19 09:39:19 +00:00
{
2005-01-06 22:31:58 +00:00
Vehicle * v = GetVehicle ( data_a ) ;
2005-01-19 10:00:37 +00:00
return ( v - > u . rail . track = = 0x80 & & ( v - > vehstatus | VS_STOPPED ) ) ;
2004-12-19 09:39:19 +00:00
}
2005-06-24 12:38:35 +00:00
void TrainEnterDepot ( Vehicle * v , TileIndex tile )
2004-08-09 17:04:08 +00:00
{
2005-10-05 07:20:26 +00:00
SetSignalsOnBothDir ( tile , _depot_track_ind [ GB ( _m [ tile ] . m5 , 0 , 2 ) ] ) ;
2004-08-09 17:04:08 +00:00
2005-11-18 23:41:03 +00:00
if ( ! IsFrontEngine ( v ) ) v = GetFirstVehicleInChain ( v ) ;
2004-08-09 17:04:08 +00:00
2004-12-09 21:46:56 +00:00
VehicleServiceInDepot ( v ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
2004-11-17 08:52:47 +00:00
TriggerVehicle ( v , VEHICLE_TRIGGER_DEPOT ) ;
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
Order t ;
2004-09-10 19:02:27 +00:00
2004-12-05 12:43:04 +00:00
InvalidateWindow ( WC_VEHICLE_VIEW , v - > index ) ;
2004-08-09 17:04:08 +00:00
2004-12-05 12:43:04 +00:00
t = v - > current_order ;
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-08-09 17:04:08 +00:00
2005-03-20 08:43:29 +00:00
if ( HASBIT ( t . flags , OFB_PART_OF_ORDERS ) ) { // Part of the orderlist?
2004-12-05 12:43:04 +00:00
v - > u . rail . days_since_order_progr = 0 ;
v - > cur_order_index + + ;
2005-03-20 08:43:29 +00:00
} else if ( HASBIT ( t . flags , OFB_HALT_IN_DEPOT ) ) { // User initiated?
2004-08-09 17:04:08 +00:00
v - > vehstatus | = VS_STOPPED ;
if ( v - > owner = = _local_player ) {
2004-12-02 22:53:07 +00:00
SetDParam ( 0 , v - > unitnumber ) ;
2004-12-19 09:39:19 +00:00
AddValidatedNewsItem (
2004-08-09 17:04:08 +00:00
STR_8814_TRAIN_IS_WAITING_IN_DEPOT ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
2004-12-19 09:39:19 +00:00
0 ,
ValidateTrainInDepot ) ;
2004-08-09 17:04:08 +00:00
}
}
}
2005-01-24 22:24:47 +00:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
}
static void CheckIfTrainNeedsService ( Vehicle * v )
{
2005-11-13 13:43:55 +00:00
const Depot * depot ;
2004-09-23 21:20:38 +00:00
TrainFindDepotData tfdd ;
2004-08-09 17:04:08 +00:00
2005-11-14 19:48:04 +00:00
if ( PBSTileReserved ( v - > tile ) & v - > u . rail . track ) return ;
if ( v - > u . rail . pbs_status = = PBS_STAT_HAS_PATH ) return ;
if ( _patches . servint_trains = = 0 ) return ;
if ( ! VehicleNeedsService ( v ) ) return ;
if ( v - > vehstatus & VS_STOPPED ) return ;
if ( _patches . gotodepot & & VehicleHasDepotOrders ( v ) ) return ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// Don't interfere with a depot visit scheduled by the user, or a
// depot visit by the order list.
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
2005-03-20 08:43:29 +00:00
( v - > current_order . flags & ( OF_HALT_IN_DEPOT | OF_PART_OF_ORDERS ) ) ! = 0 )
2004-08-09 17:04:08 +00:00
return ;
2004-09-23 21:20:38 +00: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 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT ) {
2004-09-23 21:20:38 +00:00
/* If we were already heading for a depot but it has
* suddenly moved farther away , we continue our normal
* schedule ? */
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_DUMMY ;
v - > current_order . flags = 0 ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
return ;
}
2004-09-23 21:20:38 +00:00
depot = GetDepotByTile ( tfdd . tile ) ;
2004-08-09 17:04:08 +00:00
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_DEPOT & &
2005-02-06 10:18:47 +00:00
v - > current_order . station ! = depot - > index & &
2004-12-05 12:43:04 +00:00
! CHANCE16 ( 3 , 16 ) )
2004-08-09 17:04:08 +00:00
return ;
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_GOTO_DEPOT ;
v - > current_order . flags = OF_NON_STOP ;
2005-02-06 10:18:47 +00:00
v - > current_order . station = depot - > index ;
2004-09-23 21:20:38 +00:00
v - > dest_tile = tfdd . tile ;
2004-12-21 23:27:58 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2004-08-09 17:04:08 +00:00
}
2005-05-11 16:17:03 +00:00
int32 GetTrainRunningCost ( const Vehicle * v )
2004-08-09 17:04:08 +00:00
{
int32 cost = 0 ;
do {
2004-12-04 07:41:37 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
2004-08-09 17:04:08 +00: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-11-18 23:41:03 +00:00
if ( IsFrontEngine ( v ) ) {
2004-08-09 17:04:08 +00:00
CheckVehicleBreakdown ( v ) ;
AgeVehicle ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
CheckIfTrainNeedsService ( v ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
// check if train hasn't advanced in its order list for a set number of days
2005-01-18 16:01:35 +00: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 17:04:08 +00:00
v - > u . rail . days_since_order_progr = 0 ;
2004-12-02 22:53:07 +00:00
SetDParam ( 0 , v - > unitnumber ) ;
2004-08-09 17:04:08 +00:00
AddNewsItem (
STR_TRAIN_IS_LOST ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
0 ) ;
}
2005-01-17 21:54:45 +00:00
CheckOrders ( v - > index , OC_INIT ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* update destination */
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_STATION & &
2005-11-14 19:48:04 +00:00
( tile = GetStation ( v - > current_order . station ) - > train_tile ) ! = 0 ) {
v - > dest_tile = tile ;
}
2004-08-09 17:04:08 +00:00
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 22:24:47 +00:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
}
}
}
2005-01-22 20:23:18 +00:00
void TrainsYearlyLoop ( void )
2004-08-09 17:04:08 +00:00
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
2005-11-18 23:41:03 +00:00
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) ) {
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00: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 22:53:07 +00:00
SetDParam ( 1 , v - > profit_this_year ) ;
SetDParam ( 0 , v - > unitnumber ) ;
2004-08-09 17:04:08 +00: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 ) ;
}
}
}
2005-01-22 20:23:18 +00:00
void InitializeTrains ( void )
2004-08-09 17:04:08 +00:00
{
_age_cargo_skip_counter = 1 ;
}