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"
2006-03-16 15:16:27 +00:00
# include "bridge_map.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"
2006-03-24 08:55:08 +00:00
# include "station_map.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"
2006-03-06 20:55:24 +00:00
# include "tunnel_map.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-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"
2006-02-05 19:46:20 +00:00
# include "newgrf_callbacks.h"
2006-02-03 12:55:21 +00:00
# include "newgrf_engine.h"
2006-09-27 18:17:01 +00:00
# include "newgrf_sound.h"
2006-05-02 21:43:47 +00:00
# include "newgrf_text.h"
2006-03-18 13:00:32 +00:00
# include "direction.h"
2006-05-27 16:12:16 +00:00
# include "yapf/yapf.h"
2006-08-14 14:21:15 +00:00
# include "date.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
2006-08-22 14:38:37 +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 } ;
2004-08-09 17:04:08 +00:00
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 ) ;
2006-02-06 09:18:04 +00:00
uint16 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 ) )
2006-06-12 11:38:07 +00:00
vweight + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_weight ;
2005-11-05 16:07:26 +00:00
}
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 ;
}
2006-03-29 16:30:26 +00:00
/**
* Recalculates the cached total power of a train . Should be called when the consist is changed
* @ param v First vehicle of the consist .
*/
void TrainPowerChanged ( Vehicle * v )
{
Vehicle * u ;
uint32 power = 0 ;
for ( u = v ; u ! = NULL ; u = u - > next ) {
const RailVehicleInfo * rvi_u ;
bool engine_has_power = true ;
bool wagon_has_power = true ;
/* Power is not added for articulated parts */
if ( IsArticulatedPart ( u ) ) continue ;
2006-06-07 19:35:21 +00:00
if ( IsBridgeTile ( u - > tile ) & & IsBridgeMiddle ( u - > tile ) & & DiagDirToAxis ( DirToDiagDir ( u - > direction ) ) = = GetBridgeAxis ( u - > tile ) ) {
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailTypeOnBridge ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailTypeOnBridge ( u - > tile ) ) ) wagon_has_power = false ;
} else if ( IsLevelCrossingTile ( u - > tile ) ) {
2006-08-28 18:53:03 +00:00
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailTypeCrossing ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailTypeCrossing ( u - > tile ) ) ) wagon_has_power = false ;
2006-03-29 16:30:26 +00:00
} else {
if ( ! HasPowerOnRail ( u - > u . rail . railtype , GetRailType ( u - > tile ) ) ) engine_has_power = false ;
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetRailType ( u - > tile ) ) ) wagon_has_power = false ;
}
rvi_u = RailVehInfo ( u - > engine_type ) ;
if ( engine_has_power ) power + = rvi_u - > power ;
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) & & ( wagon_has_power ) ) {
2006-06-12 11:38:07 +00:00
power + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_power ;
2006-03-29 16:30:26 +00:00
}
}
if ( v - > u . rail . cached_power ! = power ) {
v - > u . rail . cached_power = power ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
2006-09-05 16:40:23 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
2006-03-29 16:30:26 +00:00
}
}
2005-06-05 15:37:00 +00:00
/**
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 ;
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 ) ;
2006-02-08 19:06:46 +00:00
first_engine = IsFrontEngine ( v ) ? v - > engine_type : INVALID_ENGINE ;
2005-11-03 09:22:24 +00:00
v - > u . rail . cached_total_length = 0 ;
2006-03-29 16:30:26 +00:00
v - > u . rail . compatible_railtypes = 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
2006-02-07 18:55:06 +00:00
// Update the v->first cache. This is faster than having to brute force it later.
if ( u - > first = = NULL ) u - > first = v ;
2005-06-06 14:26:15 +00:00
// update the 'first engine'
2006-02-08 19:06:46 +00:00
u - > u . rail . first_engine = ( v = = u ) ? INVALID_ENGINE : first_engine ;
2006-03-29 16:30:26 +00:00
u - > u . rail . railtype = GetEngine ( u - > engine_type ) - > railtype ;
2005-06-06 14:26:15 +00:00
2006-06-12 11:38:07 +00:00
if ( IsTrainEngine ( u ) ) first_engine = u - > engine_type ;
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
// check if its a powered wagon
CLRBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ;
2006-05-31 18:00:08 +00:00
/* Check powered wagon / visual effect callback */
2006-06-04 18:22:32 +00:00
if ( HASBIT ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_WAGON_POWER ) ) {
2006-05-31 18:00:08 +00:00
uint16 callback = GetVehicleCallback ( CBID_TRAIN_WAGON_POWER , 0 , 0 , u - > engine_type , u ) ;
2005-06-06 00:19:24 +00:00
2006-05-31 18:00:08 +00:00
if ( callback ! = CALLBACK_FAILED ) u - > u . rail . cached_vis_effect = callback ;
}
if ( ( rvi_v - > pow_wag_power ! = 0 ) & & ( rvi_u - > flags & RVI_WAGON ) & & UsesWagonOverride ( u ) ) {
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
}
2005-06-06 00:19:24 +00:00
}
2006-03-29 16:30:26 +00:00
/* Do not count powered wagons for the compatible railtypes, as wagons always
have railtype normal */
if ( rvi_u - > power > 0 ) {
v - > u . rail . compatible_railtypes | = GetRailTypeInfo ( u - > u . rail . railtype ) - > powered_railtypes ;
}
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 ;
2006-06-04 18:22:32 +00:00
if ( HASBIT ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_VEHICLE_LENGTH ) ) {
2006-03-31 10:14:25 +00:00
veh_len = GetVehicleCallback ( CBID_TRAIN_VEHICLE_LENGTH , 0 , 0 , u - > engine_type , u ) ;
2006-06-04 18:22:32 +00:00
}
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 ;
2006-03-29 16:30:26 +00:00
TrainPowerChanged ( v ) ;
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 ;
2006-03-24 08:55:08 +00:00
StationID sid = GetStationIndex ( tile ) ;
2005-11-13 13:43:55 +00:00
2005-01-30 19:51:39 +00:00
assert ( v - > type = = VEH_Train ) ;
//When does a train drive through a station
//first we deal with the "new nonstop handling"
2006-09-03 08:25:27 +00:00
if ( _patches . new_nonstop & & o - > flags & OF_NON_STOP & & sid = = o - > dest ) {
2005-01-30 19:51:39 +00:00
return false ;
2006-03-24 08:55:08 +00:00
}
2005-01-30 19:51:39 +00:00
2006-03-24 08:55:08 +00:00
if ( v - > last_station_visited = = sid ) return false ;
2005-01-30 19:51:39 +00:00
2006-09-03 08:25:27 +00:00
if ( sid ! = o - > dest & & ( o - > flags & OF_NON_STOP | | _patches . new_nonstop ) ) {
2005-01-30 19:51:39 +00:00
return false ;
2006-03-24 08:55:08 +00:00
}
2005-01-30 19:51:39 +00:00
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 ;
2006-08-28 18:53:03 +00:00
int num = 0 ; //number of vehicles, change this into the number of axles later
2005-01-26 12:51:04 +00:00
int power = 0 ;
int mass = 0 ;
int max_speed = 2000 ;
int area = 120 ;
int friction = 35 ; //[1e-3]
2006-08-28 18:53:03 +00:00
int drag_coeff = 20 ; //[1e-4]
2005-01-26 12:51:04 +00:00
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 + + ) {
2006-03-08 06:55:33 +00:00
Direction dir = u - > direction ;
Direction 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 ) ) {
2006-06-01 09:41:35 +00:00
uint station_length = GetPlatformLength ( v - > tile , DirToDiagDir ( v - > direction ) ) ;
2005-01-26 12:51:04 +00:00
int delta_v ;
max_speed = 120 ;
delta_v = v - > cur_speed / ( station_length + 1 ) ;
if ( v - > max_speed > ( v - > cur_speed - delta_v ) )
max_speed = v - > cur_speed - ( delta_v / 10 ) ;
max_speed = max ( max_speed , 25 * station_length ) ;
}
}
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 ;
2006-02-13 21:15:00 +00:00
if ( u - > u . rail . track = = 0x80 ) max_speed = min ( max_speed , 61 ) ;
2005-01-26 12:51:04 +00:00
2005-04-12 09:17:51 +00:00
if ( HASBIT ( u - > u . rail . flags , VRF_GOINGUP ) ) {
2006-08-28 18:53:03 +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 ;
2006-02-13 21:15:00 +00:00
} else {
2005-01-26 12:51:04 +00:00
resistance = ( area * ( drag_coeff / 2 ) * speed * speed ) / 10000 ;
2006-02-13 21:15:00 +00:00
}
2005-01-26 12:51:04 +00:00
resistance + = incl ;
resistance * = 4 ; //[N]
if ( speed > 0 ) {
switch ( v - > u . rail . railtype ) {
2005-10-16 07:58:15 +00:00
case RAILTYPE_RAIL :
2006-03-29 16:30:26 +00:00
case RAILTYPE_ELECTRIC :
2005-10-16 07:58:15 +00:00
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
}
2006-03-08 06:55:33 +00:00
int GetTrainImage ( const Vehicle * v , Direction direction )
2004-08-09 17:04:08 +00:00
{
int img = v - > spritenum ;
int base ;
2006-03-18 13:00:32 +00:00
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) direction = ReverseDir ( direction ) ;
2004-08-09 17:04:08 +00:00
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-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 ) ) {
2006-03-12 17:42:04 +00:00
image = GetCustomVehicleIcon ( engine , DIR_W ) ;
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 ) ;
}
2006-06-04 18:22:32 +00:00
static uint CountArticulatedParts ( EngineID engine_type )
2005-11-05 16:07:26 +00:00
{
uint16 callback ;
uint i ;
2006-06-04 18:22:32 +00:00
if ( ! HASBIT ( EngInfo ( engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) return 0 ;
2005-11-05 16:07:26 +00:00
for ( i = 1 ; i < 10 ; i + + ) {
2006-03-31 10:14:25 +00:00
callback = GetVehicleCallback ( CBID_TRAIN_ARTIC_ENGINE , i , 0 , 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
}
2006-06-04 18:22:32 +00:00
static void AddArticulatedParts ( Vehicle * * vl )
2005-11-05 16:07:26 +00:00
{
const RailVehicleInfo * rvi_artic ;
EngineID engine_type ;
Vehicle * v = vl [ 0 ] ;
Vehicle * u = v ;
uint16 callback ;
bool flip_image ;
uint i ;
2006-06-04 18:22:32 +00:00
if ( ! HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) return ;
2005-11-05 16:07:26 +00:00
for ( i = 1 ; i < 10 ; i + + ) {
2006-03-31 10:14:25 +00:00
callback = GetVehicleCallback ( CBID_TRAIN_ARTIC_ENGINE , i , 0 , v - > engine_type , NULL ) ;
2006-02-13 21:15:00 +00:00
if ( callback = = CALLBACK_FAILED | | callback = = 0xFF ) return ;
2005-11-05 16:07:26 +00:00
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 ;
2006-05-19 10:04:03 +00:00
u - > cargo_subtype = 0 ;
2005-11-05 16:07:26 +00:00
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 ;
2006-06-04 18:22:32 +00:00
num_vehicles = 1 + CountArticulatedParts ( engine ) ;
2005-11-05 16:07:26 +00:00
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 ;
2006-03-06 20:28:28 +00:00
DiagDirection 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 ;
2006-03-12 16:13:16 +00:00
dir = GetRailDepotDirection ( tile ) ;
2004-08-09 17:04:08 +00:00
2006-03-06 20:28:28 +00:00
v - > direction = DiagDirToDir ( dir ) ;
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 ;
2006-05-19 10:04:03 +00:00
v - > cargo_subtype = 0 ;
2004-08-09 17:04:08 +00:00
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
2006-08-20 19:05:28 +00:00
v - > build_year = _cur_year ;
2004-08-09 17:04:08 +00:00
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
2006-06-04 18:22:32 +00:00
AddArticulatedParts ( vl ) ;
2005-11-05 16:07:26 +00:00
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 ) ) ;
2006-09-08 10:47:39 +00:00
GetPlayer ( _current_player ) - > num_engines [ engine ] + + ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2006-02-04 18:25:07 +00:00
if ( IsLocalPlayer ( ) ) {
InvalidateWindow ( WC_REPLACE_VEHICLE , VEH_Train ) ; // updates the replace Train window
}
2004-08-09 17:04:08 +00:00
}
}
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 ) {
2006-04-10 07:15:58 +00:00
if ( CmdFailed ( DoCommand ( 0 , v - > index | ( u - > index < < 16 ) , 1 , DC_EXEC ,
2005-11-18 23:41:03 +00:00
CMD_MOVE_RAIL_VEHICLE ) ) )
2004-08-09 17:04:08 +00:00
break ;
}
}
}
2005-11-13 13:43:55 +00:00
static int32 EstimateTrainCost ( const RailVehicleInfo * rvi )
2004-08-09 17:04:08 +00:00
{
2006-02-13 21:15:00 +00:00
return rvi - > base_cost * ( _price . build_railvehicle > > 3 ) > > 5 ;
2004-08-09 17:04:08 +00:00
}
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 ;
2006-05-19 10:04:03 +00:00
u - > cargo_subtype = v - > cargo_subtype ;
2005-01-18 23:27:06 +00:00
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.
2006-04-10 07:15:58 +00:00
* @ param tile tile of the depot where rail - vehicle is built
2005-05-09 22:33:00 +00:00
* @ param p1 engine type id
2006-05-11 13:31:14 +00:00
* @ param p2 bit 0 when set , the train will get number 0 , otherwise it will get a free number
* bit 1 prevents any free cars from being added to the train
2004-08-09 17:04:08 +00:00
*/
2006-04-10 07:15:58 +00:00
int32 CmdBuildRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
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-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) */
2006-01-18 14:12:26 +00:00
if ( ! IsEngineBuildable ( p1 , VEH_Train ) ) return_cmd_error ( STR_ENGINE_NOT_BUILDABLE ) ;
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 */
2006-04-01 14:53:11 +00:00
/* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */
if ( ! HasPowerOnRail ( e - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
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 ;
2006-06-04 18:22:32 +00:00
num_vehicles + = CountArticulatedParts ( 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 ] ;
2006-06-29 08:58:08 +00:00
unit_num = HASBIT ( p2 , 0 ) ? 0 : GetFreeUnitNumber ( VEH_Train ) ;
2006-05-11 13:31:14 +00:00
if ( unit_num > _patches . max_trains )
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
2006-04-10 07:15:58 +00:00
DiagDirection dir = GetRailDepotDirection ( tile ) ;
int x = TileX ( tile ) * TILE_SIZE + _vehicle_initial_x_fract [ dir ] ;
int y = TileY ( tile ) * TILE_SIZE + _vehicle_initial_y_fract [ dir ] ;
2005-07-21 06:31:02 +00:00
2004-08-09 17:04:08 +00:00
v - > unitnumber = unit_num ;
2006-03-06 20:28:28 +00:00
v - > direction = DiagDirToDir ( dir ) ;
2005-06-27 06:57:24 +00:00
v - > tile = tile ;
2004-08-09 17:04:08 +00:00
v - > owner = _current_player ;
2006-04-10 07:15:58 +00:00
v - > x_pos = x ;
v - > y_pos = y ;
2004-08-09 17:04:08 +00:00
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 ;
2006-05-19 10:04:03 +00:00
v - > cargo_subtype = 0 ;
2004-08-09 17:04:08 +00:00
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 ;
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 ;
2006-08-20 19:05:28 +00:00
v - > build_year = _cur_year ;
2004-08-09 17:04:08 +00:00
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 {
2006-06-04 18:22:32 +00:00
AddArticulatedParts ( 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
2006-08-28 18:53:03 +00:00
if ( ! HASBIT ( p2 , 1 ) ) { // 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
2006-09-08 10:47:39 +00:00
GetPlayer ( _current_player ) - > num_engines [ p1 ] + + ;
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
}
}
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
2006-03-12 12:19:25 +00:00
* number of cars ( including loco ) then . If not it returns - 1 */
2006-09-27 12:17:33 +00:00
int CheckTrainInDepot ( const Vehicle * v , bool needs_to_be_stopped )
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 */
2006-03-12 12:19:25 +00:00
if ( ! IsTileDepotType ( tile , TRANSPORT_RAIL ) | | v - > cur_speed ! = 0 ) return - 1 ;
2004-08-09 17:04:08 +00:00
count = 0 ;
2005-03-09 19:09:04 +00:00
for ( ; v ! = NULL ; v = v - > next ) {
2006-01-08 16:56:41 +00:00
/* This count is used by the depot code to determine the number of engines
* in the consist . Exclude articulated parts so that autoreplacing to
2006-06-08 20:17:19 +00:00
* engines with more articulated parts than before works correctly .
2006-06-08 20:12:07 +00:00
*
* Also skip counting rear ends of multiheaded engines */
if ( ! IsArticulatedPart ( v ) & & ! ( ! IsTrainEngine ( v ) & & IsMultiheaded ( v ) ) ) count + + ;
2005-03-09 19:09:04 +00:00
if ( v - > u . rail . track ! = 0x80 | | v - > tile ! = tile | |
2006-09-03 11:49:38 +00:00
( IsFrontEngine ( v ) & & needs_to_be_stopped & & ! ( v - > vehstatus & VS_STOPPED ) ) ) {
2005-03-09 19:09:04 +00:00
return - 1 ;
}
}
2004-08-09 17:04:08 +00:00
return count ;
}
2006-09-03 11:49:38 +00:00
/* Used to check if the train is inside the depot and verifying that the VS_STOPPED flag is set */
inline int CheckTrainStoppedInDepot ( const Vehicle * v )
{
return CheckTrainInDepot ( v , true ) ;
}
/* Used to check if the train is inside the depot, but not checking the VS_STOPPED flag */
inline bool CheckTrainIsInsideDepot ( const Vehicle * v )
{
return ( CheckTrainInDepot ( v , false ) > 0 ) ;
}
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 ) {
2006-02-13 21:15:00 +00:00
if ( dst - > type = = VEH_Train & & IsFreeWagon ( dst ) & & 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 ) ) ;
2006-02-01 07:36:15 +00:00
for ( ; v ! = NULL ; v = GetNextVehicle ( v ) ) {
2005-11-18 23:41:03 +00:00
if ( ! IsMultiheaded ( v ) | | ! IsTrainEngine ( v ) ) continue ;
/* make sure that there are no free cars before next engine */
2006-02-01 07:36:15 +00:00
for ( u = v ; u - > next ! = NULL & & ! IsTrainEngine ( u - > next ) ; u = u - > next ) ;
2005-11-18 23:41:03 +00:00
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.
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-09 22:33:00 +00:00
* @ 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
*/
2006-04-10 07:15:58 +00:00
int32 CmdMoveRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
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 ;
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( 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 ) {
2006-07-30 22:55:17 +00:00
dst = IsTrainEngine ( src ) ? NULL : 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 ;
2006-02-01 07:36:15 +00:00
for ( u = dst_head ; u ! = NULL ; u = u - > next ) {
2005-11-18 23:41:03 +00:00
if ( IsTrainEngine ( u ) & & IsMultiheaded ( u ) & & u - > u . rail . other_multiheaded_part ! = NULL ) {
engine = u ;
}
2006-07-30 22:55:17 +00:00
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-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-25 21:33:57 +00:00
// when moving all wagons, we can't have the same src_head and dst_head
if ( HASBIT ( p2 , 0 ) & & src_head = = dst_head ) return 0 ;
2006-01-06 21:10:58 +00:00
{
2006-01-25 21:33:57 +00:00
int src_len = 0 ;
int max_len = _patches . mammoth_trains ? 100 : 9 ;
2004-08-09 17:04:08 +00:00
2006-01-25 21:33:57 +00:00
// check if all vehicles in the source train are stopped inside a depot.
src_len = CheckTrainStoppedInDepot ( src_head ) ;
2006-03-12 12:19:25 +00:00
if ( src_len < 0 ) return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
2004-08-09 17:04:08 +00:00
2006-01-25 21:33:57 +00:00
// check the destination row if the source and destination aren't the same.
if ( src_head ! = dst_head ) {
int dst_len = 0 ;
2006-01-08 16:56:41 +00:00
2006-01-25 21:33:57 +00:00
if ( dst_head ! = NULL ) {
// check if all vehicles in the dest train are stopped.
dst_len = CheckTrainStoppedInDepot ( dst_head ) ;
2006-03-12 12:19:25 +00:00
if ( dst_len < 0 ) return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
2006-01-08 16:56:41 +00:00
2006-01-25 21:33:57 +00:00
assert ( dst_head - > tile = = src_head - > tile ) ;
}
2006-01-06 21:10:58 +00:00
2006-01-25 21:33:57 +00:00
// We are moving between rows, so only count the wagons from the source
// row that are being moved.
if ( HASBIT ( p2 , 0 ) ) {
const Vehicle * u ;
for ( u = src_head ; u ! = src & & u ! = NULL ; u = GetNextVehicle ( u ) )
src_len - - ;
} else {
// If moving only one vehicle, just count that.
src_len = 1 ;
}
2006-01-06 21:10:58 +00:00
2006-01-25 21:33:57 +00:00
if ( src_len + dst_len > max_len ) {
// Abort if we're adding too many wagons to a train.
if ( dst_head ! = NULL & & IsFrontEngine ( dst_head ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
// Abort if we're making a train on a new row.
if ( dst_head = = NULL & & IsTrainEngine ( src ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
}
} else {
// Abort if we're creating a new train on an existing row.
if ( src_len > max_len & & src = = src_head & & IsTrainEngine ( GetNextVehicle ( src_head ) ) )
return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
2006-01-06 21:10:58 +00:00
}
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 ) ;
2006-02-13 21:15:00 +00:00
if ( flags & DC_EXEC ) src - > unitnumber = unit_num ;
2004-08-09 17:04:08 +00:00
}
2006-05-02 21:43:47 +00:00
if ( dst_head ! = NULL ) {
/* Check NewGRF Callback 0x1D */
uint16 callback = GetVehicleCallbackParent ( CBID_TRAIN_ALLOW_WAGON_ATTACH , 0 , 0 , dst_head - > engine_type , src , dst_head ) ;
if ( callback ! = CALLBACK_FAILED ) {
2006-05-03 06:24:14 +00:00
if ( callback = = 0xFD ) return_cmd_error ( STR_INCOMPATIBLE_RAIL_TYPES ) ;
2006-05-02 21:43:47 +00:00
if ( callback < 0xFD ) {
StringID error = GetGRFStringID ( GetEngineGRFID ( dst_head - > engine_type ) , 0xD000 + callback ) ;
return_cmd_error ( error ) ;
}
}
}
2004-08-09 17:04:08 +00:00
/* 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
2006-07-30 22:55:17 +00:00
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 ) ;
2006-07-30 22:43:50 +00:00
// previous line set the front engine to the old front. We need to clear that
src - > u . rail . other_multiheaded_part - > first = NULL ;
2005-11-18 23:41:03 +00:00
}
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 ) ) {
2006-04-10 07:15:58 +00:00
CmdMoveRailVehicle ( 0 , flags , src_head - > index | ( INVALID_VEHICLE < < 16 ) , 1 ) ;
2006-08-28 18:53:03 +00:00
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
}
2006-07-30 22:55:17 +00:00
if ( src_head ! = NULL ) {
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
2006-07-30 22:55:17 +00:00
if ( dst_head ! = NULL ) {
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.
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-09 22:33:00 +00:00
* @ param p1 train to start / stop
* @ param p2 unused
*/
2006-04-10 07:15:58 +00:00
int32 CmdStartStopTrain ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
Vehicle * v ;
2006-08-09 21:11:45 +00:00
uint16 callback ;
2004-08-09 17:04:08 +00:00
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +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
2006-08-09 21:11:45 +00:00
/* Check if this train can be started/stopped. The callback will fail or
* return 0xFF if it can . */
callback = GetVehicleCallback ( CBID_VEHICLE_START_STOP_CHECK , 0 , 0 , v - > engine_type , v ) ;
if ( callback ! = CALLBACK_FAILED & & callback ! = 0xFF ) {
StringID error = GetGRFStringID ( GetEngineGRFID ( v - > engine_type ) , 0xD000 + callback ) ;
return_cmd_error ( error ) ;
}
2006-08-12 10:41:29 +00:00
if ( v - > vehstatus & VS_STOPPED & & v - > u . rail . cached_power = = 0 ) return_cmd_error ( STR_TRAIN_START_NO_CATENARY ) ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
2006-03-04 11:01:35 +00:00
if ( v - > vehstatus & VS_STOPPED & & v - > u . rail . track = = 0x80 ) {
DeleteVehicleNews ( p1 , STR_8814_TRAIN_IS_WAITING_IN_DEPOT ) ;
}
2004-08-09 17:04:08 +00:00
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.
2006-04-10 07:15:58 +00:00
* @ param tile 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
*/
2006-04-10 07:15:58 +00:00
int32 CmdSellRailWagon ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
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
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( 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
2006-03-12 12:19:25 +00:00
if ( CheckTrainStoppedInDepot ( first ) < 0 ) {
return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
}
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
2006-09-04 20:40:33 +00:00
* 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 */
2005-05-05 20:46:14 +00:00
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 ;
2006-04-21 17:15:05 +00:00
if ( first - > prev_shared ! = NULL ) {
first - > prev_shared - > next_shared = new_f ;
new_f - > prev_shared = first - > prev_shared ;
}
if ( first - > next_shared ! = NULL ) {
first - > next_shared - > prev_shared = new_f ;
new_f - > next_shared = first - > next_shared ;
}
2005-05-05 20:46:14 +00:00
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 ) ;
2006-04-10 07:15:58 +00:00
DoCommand ( v - > tile , v - > index | INVALID_VEHICLE < < 16 , 0 , DC_EXEC , CMD_MOVE_RAIL_VEHICLE ) ;
2005-05-05 20:46:14 +00:00
}
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 */
2006-02-13 21:15:00 +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 ) ;
2006-02-13 21:15:00 +00:00
if ( IsFrontEngine ( first ) ) 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 ;
}
2006-03-06 20:28:28 +00:00
static void UpdateTrainDeltaXY ( Vehicle * v , Direction direction )
2004-08-09 17:04:08 +00:00
{
# 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 ;
2006-02-13 21:15:00 +00:00
if ( _patches . vehicle_speed | | ( old = = 0 ) ! = ( spd = = 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
}
}
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 */
2006-03-08 06:55:33 +00:00
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction = ReverseDir ( a - > direction ) ;
if ( ! ( b - > u . rail . track & 0x80 ) ) b - > direction = ReverseDir ( b - > direction ) ;
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
2006-06-07 19:35:21 +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 {
2006-03-08 06:55:33 +00:00
if ( ! ( a - > u . rail . track & 0x80 ) ) a - > direction = ReverseDir ( a - > direction ) ;
2004-09-10 19:02:27 +00:00
UpdateVarsAfterSwap ( a ) ;
2005-03-25 12:07:26 +00:00
2006-06-07 19:35:21 +00:00
VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
2004-08-09 17:04:08 +00:00
}
2006-03-29 16:30:26 +00:00
/* Update train's power incase tiles were different rail type */
TrainPowerChanged ( v ) ;
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 )
{
2006-04-15 20:07:42 +00:00
if ( IsLevelCrossingTile ( tile ) & &
2005-11-14 19:48:04 +00:00
VehicleFromPos ( tile , & tile , TestTrainOnCrossing ) = = NULL & & // empty?
2006-03-24 15:24:16 +00:00
IsCrossingBarred ( tile ) ) {
UnbarCrossing ( tile ) ;
2005-11-14 19:48:04 +00:00
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
2006-08-28 10:14:37 +00:00
// start<>end, start+1<>end-1, ...
2005-06-06 22:44:11 +00:00
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 ;
}
}
2006-02-01 06:32:03 +00:00
2004-08-09 17:04:08 +00:00
static void ReverseTrainDirection ( Vehicle * v )
{
int l = 0 , r = - 1 ;
Vehicle * u ;
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 ) ;
2004-12-21 16:02:14 +00:00
/* Check if we were approaching a rail/road-crossing */
{
TileIndex tile = v - > tile ;
2006-03-08 08:51:26 +00:00
DiagDirection dir = DirToDiagDir ( v - > direction ) ;
2005-03-08 19:54:10 +00:00
/* Determine the diagonal direction in which we will exit this tile */
2006-03-08 08:51:26 +00:00
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ dir ] ) {
dir = ChangeDiagDir ( dir , DIAGDIRDIFF_90LEFT ) ;
2004-12-21 16:02:14 +00:00
}
/* Calculate next tile */
2006-09-05 23:21:41 +00:00
tile + = TileOffsByDiagDir ( dir ) ;
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.
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-09 22:33:00 +00:00
* @ param p1 train to reverse
2006-03-18 13:00:32 +00:00
* @ param p2 if true , reverse a unit in a train ( needs to be in a depot )
2005-05-09 22:33:00 +00:00
*/
2006-04-10 07:15:58 +00:00
int32 CmdReverseTrainDirection ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
Vehicle * v ;
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +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
2006-03-18 13:00:32 +00:00
if ( p2 ) {
2006-03-18 13:15:34 +00:00
// turn a single unit around
2006-03-18 13:00:32 +00:00
Vehicle * front ;
2006-06-04 18:22:32 +00:00
if ( IsMultiheaded ( v ) | | HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_ARTIC_ENGINE ) ) {
2006-03-18 13:00:32 +00:00
return_cmd_error ( STR_ONLY_TURN_SINGLE_UNIT ) ;
}
front = GetFirstVehicleInChain ( v ) ;
// make sure the vehicle is stopped in the depot
if ( CheckTrainStoppedInDepot ( front ) < 0 ) {
return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
}
2004-08-09 17:04:08 +00:00
2006-03-18 13:15:34 +00:00
if ( flags & DC_EXEC ) {
2006-03-18 14:35:54 +00:00
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ;
2006-03-18 13:15:34 +00:00
}
} else {
//turn the whole train around
if ( v - > u . rail . crash_anim_pos ! = 0 | | v - > breakdown_ctr ! = 0 ) return CMD_ERROR ;
if ( flags & DC_EXEC ) {
2006-03-18 13:00:32 +00:00
if ( _patches . realistic_acceleration & & v - > cur_speed ! = 0 ) {
TOGGLEBIT ( v - > u . rail . flags , VRF_REVERSING ) ;
} else {
v - > cur_speed = 0 ;
SetLastSpeed ( v , 0 ) ;
ReverseTrainDirection ( v ) ;
}
2004-08-09 17:04:08 +00:00
}
}
return 0 ;
}
2005-05-09 22:33:00 +00:00
/** Force a train through a red signal
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-09 22:33:00 +00:00
* @ param p1 train to ignore the red signal
* @ param p2 unused
*/
2006-04-10 07:15:58 +00:00
int32 CmdForceTrainProceed ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
Vehicle * v ;
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +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-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.
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-14 12:36:16 +00:00
* @ param p1 vehicle ID of the train to refit
2006-06-04 17:38:48 +00:00
* param p2 various bitstuffed elements
* - p2 = ( bit 0 - 7 ) - the new cargo type to refit to
* - p2 = ( bit 8 - 15 ) - the new cargo subtype to refit to
2005-05-14 12:36:16 +00:00
*/
2006-04-10 07:15:58 +00:00
int32 CmdRefitRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
2005-11-14 08:09:57 +00:00
CargoID new_cid = GB ( p2 , 0 , 8 ) ;
2006-06-04 17:38:48 +00:00
byte new_subtype = GB ( p2 , 8 , 8 ) ;
2004-08-09 17:04:08 +00:00
Vehicle * v ;
int32 cost ;
uint num ;
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( 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 ;
2006-06-04 18:22:32 +00:00
if ( HASBIT ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_REFIT_CAPACITY ) ) {
2006-06-09 07:03:53 +00:00
/* Back up the vehicle's cargo type */
2005-06-03 10:39:30 +00:00
CargoID temp_cid = v - > cargo_type ;
2006-06-09 07:03:53 +00:00
byte temp_subtype = v - > cargo_subtype ;
2005-06-03 10:39:30 +00:00
v - > cargo_type = new_cid ;
2006-06-09 07:03:53 +00:00
v - > cargo_subtype = new_subtype ;
/* Check the refit capacity callback */
2006-03-31 10:14:25 +00:00
amount = GetVehicleCallback ( CBID_VEHICLE_REFIT_CAPACITY , 0 , 0 , v - > engine_type , v ) ;
2006-06-09 07:03:53 +00:00
/* Restore the original cargo type */
2005-06-03 10:39:30 +00:00
v - > cargo_type = temp_cid ;
2006-06-09 07:03:53 +00:00
v - > cargo_subtype = temp_subtype ;
2005-06-03 10:39:30 +00:00
}
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 ;
2006-02-13 21:15:00 +00:00
/* normally, the capacity depends on the cargo type, a rail vehicle can
* carry twice as much mail / goods as normal cargo , and four times as
* many passengers
*/
2005-06-01 11:34:37 +00:00
amount = rvi - > capacity ;
2006-02-13 21:15:00 +00:00
switch ( old_cid ) {
case CT_PASSENGERS : break ;
case CT_MAIL :
case CT_GOODS : amount * = 2 ; break ;
default : amount * = 4 ; break ;
}
switch ( new_cid ) {
case CT_PASSENGERS : break ;
case CT_MAIL :
case CT_GOODS : amount / = 2 ; break ;
default : amount / = 4 ; break ;
}
2005-06-01 11:34:37 +00:00
} ;
if ( amount ! = 0 ) {
2006-08-09 20:44:23 +00:00
if ( new_cid ! = v - > cargo_type ) {
2006-09-04 09:07:52 +00:00
cost + = GetRefitCost ( v - > engine_type ) ;
2006-08-09 20:44:23 +00:00
}
2005-06-01 11:34:37 +00:00
num + = amount ;
if ( flags & DC_EXEC ) {
2006-06-04 17:49:16 +00:00
v - > cargo_count = ( v - > cargo_type = = new_cid ) ? min ( amount , v - > cargo_count ) : 0 ;
2005-06-01 11:34:37 +00:00
v - > cargo_type = new_cid ;
v - > cargo_cap = amount ;
2006-06-04 17:38:48 +00:00
v - > cargo_subtype = new_subtype ;
2005-06-01 11:34:37 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2006-06-04 17:38:48 +00:00
RebuildVehicleLists ( ) ;
2005-06-01 11:34:37 +00:00
}
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
}
2006-02-13 21:15:00 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
2006-02-11 09:24:51 +00:00
_returned_refit_capacity = num ;
2004-08-09 17:04:08 +00:00
2006-10-02 22:10:04 +00:00
/* Update the train's cached variables */
if ( flags & DC_EXEC ) TrainConsistChanged ( GetVehicle ( p1 ) ) ;
2004-08-09 17:04:08 +00:00
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
{
2006-03-19 13:48:08 +00:00
if ( IsTileType ( tile , MP_RAILWAY ) & &
IsTileOwner ( tile , tfdd - > owner ) & &
2006-07-27 05:30:53 +00:00
IsRailDepot ( tile ) ) {
2006-08-26 21:54:04 +00:00
/* approximate number of tiles by dividing by DIAG_FACTOR */
tfdd - > best_length = length / DIAG_FACTOR ;
2006-03-19 13:48:08 +00:00
tfdd - > tile = tile ;
return true ;
2004-08-09 17:04:08 +00:00
}
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!
2006-05-27 16:12:16 +00:00
static TrainFindDepotData FindClosestTrainDepot ( Vehicle * v , int max_distance )
2004-08-09 17:04:08 +00:00
{
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 ;
}
2006-06-07 19:35:21 +00:00
if ( v - > u . rail . track = = 0x40 ) tile = GetVehicleOutOfTunnelTile ( v ) ;
2006-05-27 16:12:16 +00:00
if ( _patches . yapf . rail_use_yapf ) {
bool found = YapfFindNearestRailDepotTwoWay ( v , max_distance , NPF_INFINITE_PENALTY , & tfdd . tile , & tfdd . reverse ) ;
tfdd . best_length = found ? max_distance / 2 : - 1 ; // some fake distance or NOT_FOUND
} else if ( _patches . new_pathfinding_all ) {
2005-01-31 11:23:10 +00:00
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 ) ;
2006-03-29 16:30:26 +00:00
ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes , 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
2006-09-04 20:40:33 +00:00
* 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 ;
2006-02-13 21:15:00 +00:00
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.
2006-03-06 20:28:28 +00:00
DiagDirection i ;
i = DirToDiagDir ( v - > direction ) ;
2006-03-08 08:51:26 +00:00
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) {
i = ChangeDiagDir ( i , DIAGDIRDIFF_90LEFT ) ;
}
2006-03-29 16:30:26 +00:00
NewTrainPathfind ( tile , 0 , v - > u . rail . compatible_railtypes , 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
2006-03-06 20:28:28 +00:00
i = ReverseDiagDir ( DirToDiagDir ( v - > direction ) ) ;
2006-03-08 08:51:26 +00:00
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ i ] ) {
i = ChangeDiagDir ( i , DIAGDIRDIFF_90LEFT ) ;
}
2006-03-29 16:30:26 +00:00
NewTrainPathfind ( tile , 0 , v - > u . rail . compatible_railtypes , 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
2006-04-10 07:15:58 +00:00
* @ param tile unused
2005-05-09 22:33:00 +00:00
* @ param p1 train to send to the depot
2006-08-29 23:39:57 +00:00
* @ param p2 various bitmasked elements
2006-09-01 10:24:15 +00:00
* - p2 bit 0 - 3 - DEPOT_ flags ( see vehicle . h )
* - p2 bit 8 - 10 - VLW flag ( for mass goto depot )
2005-05-09 22:33:00 +00:00
*/
2006-04-10 07:15:58 +00:00
int32 CmdSendTrainToDepot ( TileIndex tile , 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
2006-09-01 10:24:15 +00:00
if ( p2 & DEPOT_MASS_SEND ) {
/* Mass goto depot requested */
2006-09-04 15:16:58 +00:00
if ( ! ValidVLWFlags ( p2 & VLW_MASK ) ) return CMD_ERROR ;
return SendAllVehiclesToDepot ( VEH_Train , flags , p2 & DEPOT_SERVICE , _current_player , ( p2 & VLW_MASK ) , p1 ) ;
2006-08-30 21:39:01 +00:00
}
2006-09-01 10:24:15 +00:00
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +00:00
v = GetVehicle ( p1 ) ;
2006-09-01 10:24:15 +00:00
if ( v - > type ! = VEH_Train | | ! CheckOwnership ( v - > owner ) ) return CMD_ERROR ;
2006-08-29 23:39:57 +00:00
2006-09-01 10:24:15 +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 ) {
2006-09-02 09:35:03 +00:00
if ( ! ! ( p2 & DEPOT_SERVICE ) = = HASBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ) {
2006-09-01 12:37:03 +00:00
/* We called with a different DEPOT_SERVICE setting.
2006-09-04 20:40:33 +00:00
* Now we change the setting to apply the new one and let the vehicle head for the same depot .
* Note : the if is ( true for requesting service = = true for ordered to stop in depot ) */
2006-09-01 12:37:03 +00:00
if ( flags & DC_EXEC ) {
TOGGLEBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , STATUS_BAR ) ;
}
return 0 ;
}
2006-09-01 10:24:15 +00:00
if ( p2 & DEPOT_DONT_CANCEL ) return CMD_ERROR ; // Requested no cancelation of depot orders
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 ;
}
2006-09-02 22:47:45 +00:00
/* check if at a standstill (not stopped only) in a depot
* the check is down here to make it possible to alter stop / service for trains entering the depot */
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) & & v - > cur_speed = = 0 ) return CMD_ERROR ;
2006-05-27 16:12:16 +00:00
tfdd = FindClosestTrainDepot ( v , 0 ) ;
2006-09-01 10:24:15 +00:00
if ( tfdd . best_length = = ( uint ) - 1 ) return_cmd_error ( STR_883A_UNABLE_TO_FIND_ROUTE_TO ) ;
2004-08-09 17:04:08 +00:00
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 ;
2006-08-27 09:28:52 +00:00
v - > current_order . flags = OF_NON_STOP ;
2006-09-01 10:24:15 +00:00
if ( ! ( p2 & DEPOT_SERVICE ) ) SETBIT ( v - > current_order . flags , OFB_HALT_IN_DEPOT ) ;
2006-09-03 08:25:27 +00:00
v - > current_order . dest = 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 )
2006-04-10 07:15:58 +00:00
DoCommand ( 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 ;
2006-09-27 18:17:01 +00:00
bool sound = false ;
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 | |
2006-03-29 16:30:26 +00:00
GetEngine ( engtype ) - > railtype > RAILTYPE_ELECTRIC | |
2006-04-14 10:48:54 +00:00
v - > vehstatus & VS_HIDDEN ) {
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
2006-04-19 16:10:17 +00:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) | | IsTunnelTile ( v - > tile ) ) continue ;
// No sparks for electric vehicles on nonelectrified tracks
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetTileRailType ( v - > tile , GetVehicleTrackdir ( v ) ) ) ) continue ;
2005-11-07 13:02:33 +00:00
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 ;
2006-03-18 13:00:32 +00:00
if ( HASBIT ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) {
x = - x ;
y = - y ;
}
2005-11-04 12:58:18 +00:00
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 ) ;
2006-09-27 18:17:01 +00:00
sound = true ;
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 ) ;
2006-09-27 18:17:01 +00:00
sound = true ;
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 ) ;
2006-09-27 18:17:01 +00:00
sound = true ;
2004-08-09 17:04:08 +00:00
}
break ;
}
2005-11-14 09:21:05 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2006-09-27 18:17:01 +00:00
if ( sound ) PlayVehicleSound ( u , VSE_TRAIN_EFFECT ) ;
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
2006-09-27 18:17:01 +00:00
if ( PlayVehicleSound ( v , VSE_START ) ) return ;
2005-06-07 18:13:49 +00:00
switch ( GetEngine ( engtype ) - > railtype ) {
2005-10-16 07:58:15 +00:00
case RAILTYPE_RAIL :
2006-03-29 16:30:26 +00:00
case RAILTYPE_ELECTRIC :
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
2006-02-13 21:15:00 +00:00
case RAILTYPE_MONO : SndPlayVehicleFx ( SND_47_MAGLEV_2 , v ) ; break ;
case RAILTYPE_MAGLEV : 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
2006-08-12 11:08:02 +00:00
// if the train got no power, then keep it in the depot
if ( v - > u . rail . cached_power = = 0 ) {
v - > vehstatus | = VS_STOPPED ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
return true ;
}
2004-08-09 17:04:08 +00:00
if ( v - > u . rail . force_proceed = = 0 ) {
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 ;
2006-03-09 12:32:25 +00:00
if ( UpdateSignalsOnSegment ( v - > tile , DirToDiagDir ( v - > direction ) ) ) {
2005-01-24 22:24:47 +00:00
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
}
(svn r3472) - [PBS] Remove from trunk. Anyone interested can still find it in branch/pbs. This reverts revisions r3158, r3140, r3075, r2977, r2674, r2625, r2621, r2529, r2528, r2525, r2524, r2519, r2517, r2516, r2507, r2499. (in conjunction with Tron)
- The only change is that the nsignalsw.grf file is kept and that existing nightlies with PBS signals get those signals converted to combo-signals.
2006-01-29 18:57:26 +00:00
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 ) ;
2006-03-09 12:32:25 +00:00
UpdateSignalsOnSegment ( v - > tile , DirToDiagDir ( v - > direction ) ) ;
2004-08-09 17:04:08 +00:00
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?
2006-02-01 06:32:03 +00:00
if ( ttfd - > dest_coords = = 0 ) return false ;
2004-08-09 17:04:08 +00:00
// did we reach the final station?
2006-03-24 08:55:08 +00:00
if ( ( ttfd - > station_index = = INVALID_STATION & & tile = = ttfd - > dest_coords ) | | (
IsTileType ( tile , MP_STATION ) & &
2006-03-26 14:41:39 +00:00
IsRailwayStation ( tile ) & &
2006-03-24 08:55:08 +00:00
GetStationIndex ( tile ) = = 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 ) {
2006-09-03 08:25:27 +00:00
fd - > station_index = v - > current_order . dest ;
2005-11-14 19:48:04 +00:00
} else {
fd - > station_index = INVALID_STATION ;
}
2004-08-09 17:04:08 +00:00
}
static const byte _initial_tile_subcoord [ 6 ] [ 4 ] [ 3 ] = {
2006-08-22 14:38:37 +00:00
{ { 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 } } ,
2004-08-09 17:04:08 +00:00
} ;
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-07-12 20:28:19 +00:00
2004-08-09 17:04:08 +00:00
/* choose a track */
2006-03-06 20:28:28 +00:00
static byte ChooseTrainTrack ( Vehicle * v , TileIndex tile , DiagDirection 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
2006-03-26 21:15:09 +00:00
TIC ( )
2004-08-09 17:04:08 +00:00
# endif
2006-02-13 21:15:00 +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 */
2006-02-13 21:15:00 +00:00
if ( KILL_FIRST_BIT ( trackdirbits ) = = 0 ) return FIND_FIRST_BIT ( trackdirbits ) ;
2004-08-09 17:04:08 +00:00
2006-05-27 16:12:16 +00:00
if ( _patches . yapf . rail_use_yapf ) {
Trackdir trackdir = YapfChooseRailTrack ( v , tile , enterdir , trackdirbits ) ;
if ( trackdir ! = INVALID_TRACKDIR ) {
best_track = TrackdirToTrack ( trackdir ) ;
} else {
best_track = FIND_FIRST_BIT ( TrackdirBitsToTrackBits ( trackdirbits ) ) ;
}
} else if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
void * perf = NpfBeginInterval ( ) ;
int time = 0 ;
2005-01-31 11:23:10 +00:00
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-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 ) ;
2006-09-05 23:21:41 +00:00
ftd = NPFRouteToStationOrTile ( tile - TileOffsByDiagDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes ) ;
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
}
2006-05-27 16:12:16 +00:00
time = NpfEndInterval ( perf ) ;
2006-06-04 09:13:59 +00:00
DEBUG ( yapf , 4 ) ( " [YAPF][NPFT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , _aystar_stats_open_size , _aystar_stats_closed_size ) ;
2004-08-09 17:04:08 +00:00
} else {
2006-05-27 16:12:16 +00:00
void * perf = NpfBeginInterval ( ) ;
int time = 0 ;
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
2006-09-05 23:21:41 +00:00
NewTrainPathfind ( tile - TileOffsByDiagDir ( enterdir ) , v - > dest_tile ,
2006-03-29 16:30:26 +00:00
v - > u . rail . compatible_railtypes , 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
}
2006-05-27 16:12:16 +00:00
time = NpfEndInterval ( perf ) ;
2006-06-04 09:13:59 +00:00
DEBUG ( yapf , 4 ) ( " [YAPF][NTPT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , 0 , 0 ) ;
2004-08-09 17:04:08 +00:00
}
2005-12-14 09:15:06 +00:00
# ifdef PF_BENCHMARK
2006-03-26 21:15:09 +00:00
TOC ( " PF time = " , 1 )
2004-08-09 17:04:08 +00:00
# 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 ) ;
2006-03-08 08:28:48 +00:00
i = _search_directions [ FIND_FIRST_BIT ( v - > u . rail . track ) ] [ DirToDiagDir ( v - > direction ) ] ;
2004-08-09 17:04:08 +00:00
2006-05-27 16:12:16 +00:00
if ( _patches . yapf . rail_use_yapf ) {
reverse_best = YapfCheckReverseTrain ( v ) ;
} else if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
2005-01-31 11:23:10 +00:00
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 ) ;
2006-03-29 16:30:26 +00:00
ftd = NPFRouteToStationOrTileTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , & fstd , TRANSPORT_RAIL , v - > owner , v - > u . rail . compatible_railtypes ) ;
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 {
2006-02-13 21:15:00 +00:00
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) ) {
2005-01-31 11:23:10 +00:00
reverse_best = true ;
2006-02-13 21:15:00 +00:00
} else {
2005-01-31 11:23:10 +00:00
reverse_best = false ;
2006-02-13 21:15:00 +00:00
}
2005-01-31 11:23:10 +00:00
}
} else {
2006-02-01 07:36:15 +00:00
for ( ; ; ) {
2005-01-31 11:23:10 +00:00
fd . best_bird_dist = ( uint ) - 1 ;
fd . best_track_dist = ( uint ) - 1 ;
2006-03-29 16:30:26 +00:00
NewTrainPathfind ( v - > tile , v - > dest_tile , v - > u . rail . compatible_railtypes , 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 : ;
2006-02-13 21:15:00 +00:00
if ( reverse ! = 0 ) break ;
2005-01-31 11:23:10 +00:00
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 ;
2006-06-02 13:15:50 +00:00
bool at_waypoint = false ;
2004-08-09 17:04:08 +00:00
2006-02-10 06:33:56 +00:00
switch ( v - > current_order . type ) {
case OT_GOTO_DEPOT :
if ( ! ( v - > current_order . flags & OF_PART_OF_ORDERS ) ) return false ;
if ( ( v - > current_order . flags & OF_SERVICE_IF_NEEDED ) & &
! VehicleNeedsService ( v ) ) {
v - > cur_order_index + + ;
}
break ;
2004-08-09 17:04:08 +00:00
2006-02-10 06:33:56 +00:00
case OT_LOADING :
case OT_LEAVESTATION :
return false ;
2006-08-22 17:13:49 +00:00
default : break ;
2004-08-09 17:04:08 +00:00
}
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 + + ;
2006-06-02 13:15:50 +00:00
at_waypoint = true ;
2004-08-09 17:04:08 +00:00
}
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 ) & &
2006-09-03 08:25:27 +00:00
v - > current_order . dest = = GetStationIndex ( v - > tile ) ) {
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.
2006-09-03 08:25:27 +00:00
if ( order - > type = = v - > current_order . type & &
order - > flags = = v - > current_order . flags & &
order - > dest = = v - > current_order . dest )
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 ;
2006-06-02 13:15:50 +00:00
InvalidateVehicleOrder ( v ) ;
2005-01-15 19:06:22 +00:00
switch ( order - > type ) {
case OT_GOTO_STATION :
2006-09-03 08:25:27 +00:00
if ( order - > dest = = v - > last_station_visited )
2005-02-02 16:16:43 +00:00
v - > last_station_visited = INVALID_STATION ;
2006-09-03 08:25:27 +00:00
v - > dest_tile = GetStation ( order - > dest ) - > xy ;
2005-01-15 19:06:22 +00:00
break ;
case OT_GOTO_DEPOT :
2006-09-03 08:25:27 +00:00
v - > dest_tile = GetDepot ( order - > dest ) - > xy ;
2005-01-15 19:06:22 +00:00
break ;
case OT_GOTO_WAYPOINT :
2006-09-03 08:25:27 +00:00
v - > dest_tile = GetWaypoint ( order - > dest ) - > xy ;
2005-01-15 19:06:22 +00:00
break ;
2004-08-09 17:04:08 +00:00
2006-06-02 13:15:50 +00:00
default :
return false ;
}
2004-08-09 17:04:08 +00:00
2006-06-02 13:15:50 +00:00
return ! at_waypoint & & CheckReverseTrain ( v ) ;
2004-08-09 17:04:08 +00:00
}
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 ,
2006-02-13 21:15:00 +00:00
0
) ;
2004-08-09 17:04:08 +00:00
}
// Did we reach the final destination?
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_GOTO_STATION & &
2006-09-03 08:25:27 +00:00
v - > current_order . dest = = 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
}
2006-09-03 08:25:27 +00:00
v - > current_order . dest = 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.
2006-06-07 19:35:21 +00:00
_get_z_hint = v - > z_pos ;
2004-08-09 17:04:08 +00:00
new_z = GetSlopeZ ( v - > x_pos , v - > y_pos ) ;
2006-06-07 19:35:21 +00:00
_get_z_hint = 0 ;
2004-08-09 17:04:08 +00:00
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
2006-03-12 15:04:03 +00:00
if ( ! IsTunnelTile ( tile ) ) {
2005-04-12 09:17:51 +00:00
SETBIT ( v - > u . rail . flags , ( new_z > old_z ) ? VRF_GOINGUP : VRF_GOINGDOWN ) ;
2006-03-12 15:04:03 +00:00
}
2005-01-09 16:02:06 +00:00
}
2004-08-09 17:04:08 +00:00
}
VehiclePositionChanged ( v ) ;
EndVehicleMove ( v ) ;
return old_z ;
}
2006-03-08 06:55:33 +00:00
static const Direction _new_vehicle_direction_table [ 11 ] = {
DIR_N , DIR_NW , DIR_W , 0 ,
DIR_NE , DIR_N , DIR_SW , 0 ,
DIR_E , DIR_SE , DIR_S
2004-08-09 17:04:08 +00:00
} ;
2006-03-06 20:28:28 +00:00
static Direction 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 ] ;
}
2006-03-06 20:28:28 +00:00
static Direction 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
2006-06-07 19:35:21 +00:00
case MP_TUNNELBRIDGE :
if ( IsBridge ( tile ) & & IsBridgeMiddle ( tile ) ) {
// is train going over the bridge?
if ( v - > z_pos > GetTileMaxZ ( tile ) ) return true ;
}
break ;
2005-01-17 09:41:46 +00:00
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 ) | |
2006-03-17 10:10:31 +00:00
IsCompatibleRail ( v - > u . rail . railtype , GetRailTypeCrossing ( tile ) )
2005-10-13 14:44:03 +00:00
) ;
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 ) | |
2006-03-29 16:30:26 +00:00
HASBIT ( v - > u . rail . compatible_railtypes , GetRailType ( tile ) )
2005-10-23 13:04:44 +00:00
) ;
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 ;
2006-03-29 16:30:26 +00:00
static const RailtypeSlowdownParams _railtype_slowdown [ ] = {
2004-08-09 17:04:08 +00:00
// normal accel
2006-08-22 14:38:37 +00:00
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // normal
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // electrified
{ 256 / 4 , 256 / 2 , 256 / 4 , 2 } , // monorail
{ 0 , 256 / 2 , 256 / 4 , 2 } , // maglev
2004-08-09 17:04:08 +00:00
} ;
/* Modify the speed of the vehicle due to a turn */
2006-03-08 06:55:33 +00:00
static void AffectSpeedByDirChange ( Vehicle * v , Direction new_dir )
2004-08-09 17:04:08 +00:00
{
2006-03-08 07:48:56 +00:00
DirDiff diff ;
2004-08-09 17:04:08 +00:00
const RailtypeSlowdownParams * rsp ;
2006-03-08 07:48:56 +00:00
if ( _patches . realistic_acceleration ) return ;
diff = DirDifference ( v - > direction , new_dir ) ;
if ( diff = = DIRDIFF_SAME ) return ;
2004-08-09 17:04:08 +00:00
rsp = & _railtype_slowdown [ v - > u . rail . railtype ] ;
2006-03-08 07:48:56 +00:00
v - > cur_speed - = ( diff = = DIRDIFF_45RIGHT | | diff = = DIRDIFF_45LEFT ? rsp - > small_turn : rsp - > large_turn ) * v - > cur_speed > > 8 ;
2004-08-09 17:04:08 +00:00
}
/* 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
}
}
2006-03-09 12:32:25 +00:00
static const DiagDirection _otherside_signal_directions [ ] = {
DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_SW , DIAGDIR_SE , 0 , 0 ,
DIAGDIR_SW , DIAGDIR_NW , DIAGDIR_NW , DIAGDIR_SW , DIAGDIR_NW , DIAGDIR_NE
2004-08-09 17:04:08 +00:00
} ;
2006-03-06 20:28:28 +00:00
static void TrainMovedChangeSignals ( TileIndex tile , DiagDirection dir )
2004-08-09 17:04:08 +00:00
{
2006-03-19 12:06:12 +00:00
if ( IsTileType ( tile , MP_RAILWAY ) & &
2006-05-09 08:17:33 +00:00
GetRailTileType ( tile ) = = RAIL_TILE_SIGNALS ) {
2006-03-19 13:48:08 +00:00
uint i = FindFirstBit2x64 ( GetTrackBits ( tile ) * 0x101 & _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 ;
2006-02-13 21:15:00 +00:00
if ( v - > u . rail . crash_anim_pos ! = 0 ) return ;
2004-08-09 17:04:08 +00:00
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 ;
2006-03-08 06:55:33 +00:00
Direction direction ;
2005-03-06 12:41:18 +00:00
} 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 ;
2006-03-08 07:48:56 +00:00
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) & & v - > tile = = vasd - > tile ) {
DirDiff diff = ChangeDirDiff ( DirDifference ( v - > direction , vasd - > direction ) , DIRDIFF_90RIGHT ) ;
2004-08-09 17:04:08 +00:00
2006-03-08 07:48:56 +00:00
if ( diff = = DIRDIFF_90RIGHT | | ( v - > cur_speed < = 5 & & diff < = DIRDIFF_REVERSE ) ) 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 ;
2006-03-06 20:28:28 +00:00
int i ;
DiagDirection enterdir ;
Direction dir ;
Direction newdir ;
2006-03-08 06:55:33 +00:00
Direction chosen_dir ;
2004-08-09 17:04:08 +00:00
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
2006-02-13 21:15:00 +00:00
if ( ! TrainCheckIfLineEnds ( v ) ) return ;
2004-12-21 16:00:14 +00:00
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 ) ;
2006-03-06 20:28:28 +00:00
enterdir = DirToDiagDir ( dir ) ;
2005-01-31 11:23:10 +00:00
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 . */
2006-02-13 21:15:00 +00:00
tracks = ts | ( ts > > 8 ) ;
2005-01-31 11:23:10 +00:00
bits = tracks & 0xFF ;
2006-05-27 16:12:16 +00:00
if ( ( _patches . new_pathfinding_all | | _patches . yapf . rail_use_yapf ) & & _patches . forbid_90_deg & & prev = = NULL ) {
2005-01-31 11:23:10 +00:00
/* 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 ) ) ;
2006-02-13 21:15:00 +00:00
}
2005-01-31 11:23:10 +00:00
2006-02-13 21:15:00 +00:00
if ( bits = = 0 ) {
2005-01-31 11:23:10 +00:00
//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 ) {
/* 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
/* 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
}
/* 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 ) ;
2006-02-13 21:15:00 +00:00
if ( r & 0x8 ) {
2005-01-31 11:23:10 +00:00
//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
2006-04-25 07:32:33 +00:00
if ( IsLevelCrossingTile ( v - > tile ) & & v - > next = = NULL ) {
UnbarCrossing ( v - > tile ) ;
MarkTileDirtyByTile ( v - > tile ) ;
}
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 ;
2006-03-29 16:30:26 +00:00
if ( GetTileRailType ( gp . new_tile , chosen_track ) ! = GetTileRailType ( gp . old_tile , v - > u . rail . track ) ) {
TrainPowerChanged ( GetFirstVehicleInChain ( v ) ) ;
}
2004-08-09 17:04:08 +00:00
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
}
2006-02-13 21:15:00 +00:00
if ( IsFrontEngine ( v ) ) 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 )
2006-03-06 20:28:28 +00:00
TrainMovedChangeSignals ( gp . old_tile , ReverseDiagDir ( enterdir ) ) ;
2004-09-10 19:02:27 +00:00
2006-02-13 21:15:00 +00:00
if ( prev = = NULL ) AffectSpeedByDirChange ( v , chosen_dir ) ;
2004-08-09 17:04:08 +00:00
v - > direction = chosen_dir ;
}
} else {
2006-06-07 19:35:21 +00:00
/* in tunnel */
2004-08-09 17:04:08 +00:00
GetNewVehiclePos ( v , & gp ) ;
2004-09-10 19:02:27 +00:00
2006-06-07 19:35:21 +00:00
// Check if to exit the tunnel...
if ( ! IsTunnelTile ( gp . new_tile ) | |
! ( VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) & 0x4 ) ) {
2005-07-19 11:42:40 +00:00
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
2006-09-22 22:15:34 +00:00
if ( ! HasSignalOnTrackdir ( gp . new_tile , ReverseTrackdir ( i ) ) ) {
2004-08-09 17:04:08 +00:00
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
2006-02-13 21:15:00 +00:00
v - > progress = 255 - 100 ;
if ( + + v - > load_unload_time_rem < _patches . wait_oneway_signal * 20 ) return ;
2006-09-22 22:15:34 +00:00
} else if ( HasSignalOnTrackdir ( gp . new_tile , 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 ) {
2006-09-05 23:21:41 +00:00
TileIndex o_tile = gp . new_tile + TileOffsByDiagDir ( enterdir ) ;
2005-03-15 12:21:59 +00:00
VehicleAtSignalData vasd ;
vasd . tile = o_tile ;
2006-03-08 06:55:33 +00:00
vasd . direction = ReverseDir ( dir ) ;
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 */
2006-02-13 21:15:00 +00:00
if ( VehicleFromPos ( o_tile , & vasd , CheckVehicleAtSignal ) = = NULL ) return ;
2004-08-09 17:04:08 +00:00
}
}
}
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-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 ) ;
2006-06-07 19:35:21 +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
2006-02-13 21:15:00 +00:00
if ( endtile = = INVALID_TILE ) return ; // tunnel is busy (error returned)
2005-03-03 23:26:35 +00:00
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 )
{
2006-03-08 08:16:31 +00:00
static const DirDiff delta [ ] = {
DIRDIFF_45LEFT , DIRDIFF_SAME , DIRDIFF_SAME , DIRDIFF_45RIGHT
} ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
do {
2006-06-07 19:35:21 +00:00
//I need to buffer the train direction
if ( ! ( v - > u . rail . track & 0x40 ) ) {
2006-06-02 13:05:41 +00:00
v - > direction = ChangeDir ( v - > direction , delta [ GB ( Random ( ) , 0 , 2 ) ] ) ;
2006-06-07 19:35:21 +00:00
}
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
2004-08-09 17:04:08 +00:00
BeginVehicleMove ( v ) ;
UpdateTrainDeltaXY ( v , v - > direction ) ;
v - > cur_image = GetTrainImage ( v , v - > direction ) ;
2006-06-07 19:35:21 +00:00
AfterSetTrainPos ( v , false ) ;
2004-08-09 17:04:08 +00:00
}
2006-02-13 21:15:00 +00:00
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
}
static void HandleCrashedTrain ( Vehicle * v )
{
2006-02-13 21:15:00 +00:00
int state = + + v - > u . rail . crash_anim_pos ;
2004-08-09 17:04:08 +00:00
uint32 r ;
Vehicle * u ;
2004-09-10 19:02:27 +00:00
2006-06-07 19:35:21 +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 ) ) {
2006-02-13 21:15:00 +00:00
int index = ( r * 10 > > 16 ) ;
2004-08-09 17:04:08 +00:00
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 ;
}
2006-02-13 21:15:00 +00:00
} while ( ( u = u - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
}
2006-02-13 21:15:00 +00:00
if ( state < = 240 & & ! ( v - > tick_counter & 3 ) ) ChangeTrainDirRandomly ( v ) ;
2004-08-09 17:04:08 +00:00
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
2006-09-27 18:17:01 +00:00
if ( ! PlayVehicleSound ( v , VSE_BREAKDOWN ) ) {
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 ;
2006-03-08 08:51:26 +00:00
DiagDirection dir ;
int t ;
2004-08-09 17:04:08 +00:00
uint32 ts ;
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 ;
2006-06-07 19:35:21 +00:00
// tunnel entrance?
if ( IsTunnelTile ( tile ) & &
DiagDirToDir ( GetTunnelDirection ( tile ) ) = = v - > direction ) {
return true ;
2006-03-06 20:55:24 +00:00
}
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 ) */
2006-09-22 22:15:34 +00:00
/* if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT)
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 */
2006-03-08 08:51:26 +00:00
dir = DirToDiagDir ( v - > direction ) ;
if ( ! ( v - > direction & 1 ) & & v - > u . rail . track ! = _state_dir_table [ dir ] ) {
dir = ChangeDiagDir ( dir , DIAGDIRDIFF_90LEFT ) ;
2004-08-09 17:04:08 +00:00
}
2004-08-11 22:07:08 +00:00
/* Calculate next tile */
2006-09-05 23:21:41 +00:00
tile + = TileOffsByDiagDir ( dir ) ;
2004-08-11 22:07:08 +00:00
// determine the track status on the next tile.
2006-03-08 08:51:26 +00:00
ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL ) & _reachable_tracks [ dir ] ;
2004-09-10 19:02:27 +00:00
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 ) {
2006-03-12 17:42:04 +00:00
case DIR_N : x = ~ x + ~ y + 24 ; break ;
case DIR_NW : x = y ; /* FALLTHROUGH */
case DIR_NE : x = ~ x + 16 ; break ;
case DIR_E : x = ~ x + y + 8 ; break ;
case DIR_SE : x = y ; break ;
case DIR_S : x = x + y - 8 ; break ;
case DIR_W : x = ~ y + x + 8 ; break ;
2004-08-09 17:04:08 +00:00
}
2005-10-03 21:20:01 +00:00
if ( GB ( ts , 0 , 16 ) ! = 0 ) {
2006-01-23 21:05:05 +00:00
/* If we approach a rail-piece which we can't enter, or the back of a depot, don't enter it! */
2006-04-23 19:35:36 +00:00
if ( x + 4 > = TILE_SIZE & &
2006-01-23 21:05:05 +00:00
( ! CheckCompatibleRail ( v , tile ) | |
( IsTileDepotType ( tile , TRANSPORT_RAIL ) & &
2006-03-12 16:13:16 +00:00
GetRailDepotDirection ( tile ) = = dir ) ) ) {
2004-12-21 16:00:14 +00:00
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
2006-04-15 20:07:42 +00:00
if ( IsLevelCrossingTile ( tile ) ) {
2006-03-24 15:24:16 +00:00
if ( ! IsCrossingBarred ( tile ) ) {
BarCrossing ( tile ) ;
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
}
2006-04-23 19:35:36 +00:00
} else if ( x + 4 > = TILE_SIZE ) {
2004-08-09 17:04:08 +00:00
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
2004-12-21 16:00:14 +00:00
return false ;
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 ] ;
2006-02-13 21:15:00 +00:00
if ( ! ( v - > direction & 1 ) ) break_speed > > = 1 ;
2005-10-03 21:20:01 +00:00
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 ;
}
2006-02-13 21:15:00 +00:00
if ( v - > u . rail . force_proceed ! = 0 ) v - > u . rail . force_proceed - - ;
2004-08-09 17:04:08 +00:00
/* 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 */
2006-02-13 21:15:00 +00:00
if ( v - > vehstatus & VS_STOPPED & & v - > cur_speed = = 0 ) return ;
2004-08-09 17:04:08 +00:00
if ( ProcessTrainOrder ( v ) ) {
v - > load_unload_time_rem = 0 ;
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
ReverseTrainDirection ( v ) ;
return ;
}
HandleTrainLoading ( v , mode ) ;
2006-02-13 21:15:00 +00:00
if ( v - > current_order . type = = OT_LOADING ) return ;
2004-08-09 17:04:08 +00:00
2006-02-13 21:15:00 +00:00
if ( CheckTrainStayInDepot ( v ) ) return ;
2004-08-09 17:04:08 +00:00
if ( ! mode ) HandleLocomotiveSmokeCloud ( v ) ;
j = UpdateTrainSpeed ( v ) ;
if ( j = = 0 ) {
// if the vehicle has speed 0, update the last_speed field.
2006-02-13 21:15:00 +00:00
if ( v - > cur_speed ! = 0 ) return ;
2004-08-09 17:04:08 +00:00
} 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
}
}
2006-05-27 16:12:16 +00:00
# define MAX_ACCEPTABLE_DEPOT_DIST 16
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 ( _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 ;
2006-09-03 11:49:38 +00:00
if ( CheckTrainIsInsideDepot ( v ) ) {
VehicleServiceInDepot ( v ) ;
return ;
}
2006-05-27 16:12:16 +00:00
tfdd = FindClosestTrainDepot ( v , MAX_ACCEPTABLE_DEPOT_DIST ) ;
2004-09-23 21:20:38 +00:00
/* Only go to the depot if it is not too far out of our way. */
2006-05-27 16:12:16 +00:00
if ( tfdd . best_length = = ( uint ) - 1 | | tfdd . best_length > MAX_ACCEPTABLE_DEPOT_DIST ) {
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 & &
2006-09-03 08:25:27 +00:00
v - > current_order . dest ! = depot - > index & &
2006-02-13 21:15:00 +00:00
! CHANCE16 ( 3 , 16 ) ) {
2004-08-09 17:04:08 +00:00
return ;
2006-02-13 21:15:00 +00:00
}
2004-08-09 17:04:08 +00:00
2004-12-05 12:43:04 +00:00
v - > current_order . type = OT_GOTO_DEPOT ;
v - > current_order . flags = OF_NON_STOP ;
2006-09-03 08:25:27 +00:00
v - > current_order . dest = 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 ) ;
2006-01-07 16:47:59 +00:00
if ( rvi - > running_cost_base > 0 )
cost + = rvi - > running_cost_base * _price . running_rail [ rvi - > running_cost_class ] ;
} while ( ( v = v - > next ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
return cost ;
}
void OnNewDay_Train ( Vehicle * v )
{
TileIndex tile ;
2006-02-13 21:15:00 +00:00
if ( ( + + v - > day_counter & 7 ) = = 0 ) DecreaseVehicleValue ( v ) ;
2004-08-09 17:04:08 +00:00
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 ) ;
}
2006-03-04 11:15:44 +00:00
CheckOrders ( v ) ;
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 & &
2006-09-03 08:25:27 +00:00
( tile = GetStation ( v - > current_order . dest ) - > train_tile ) ! = 0 ) {
2005-11-14 19:48:04 +00:00
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 ;
}
2006-01-18 15:05:01 +00:00
/*
* Link front and rear multiheaded engines to each other
* This is done when loading a savegame
*/
void ConnectMultiheadedTrains ( void )
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
v - > u . rail . other_multiheaded_part = NULL ;
}
}
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train & & IsFrontEngine ( v ) ) {
Vehicle * u = v ;
BEGIN_ENUM_WAGONS ( u ) {
if ( u - > u . rail . other_multiheaded_part ! = NULL ) continue ; // we already linked this one
if ( IsMultiheaded ( u ) ) {
if ( ! IsTrainEngine ( u ) ) {
/* we got a rear car without a front car. We will convert it to a front one */
SetTrainEngine ( u ) ;
u - > spritenum - - ;
}
{
Vehicle * w ;
2006-02-01 07:36:15 +00:00
for ( w = u - > next ; w ! = NULL & & ( w - > engine_type ! = u - > engine_type | | w - > u . rail . other_multiheaded_part ! = NULL ) ; w = GetNextVehicle ( w ) ) ;
2006-01-18 15:05:01 +00:00
if ( w ! = NULL ) {
/* we found a car to partner with this engine. Now we will make sure it face the right way */
if ( IsTrainEngine ( w ) ) {
ClearTrainEngine ( w ) ;
w - > spritenum + + ;
}
}
if ( w ! = NULL ) {
w - > u . rail . other_multiheaded_part = u ;
u - > u . rail . other_multiheaded_part = w ;
} else {
/* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
ClearMultiheaded ( u ) ;
}
}
}
} END_ENUM_WAGONS ( u )
}
}
}
/*
* Converts all trains to the new subtype format introduced in savegame 16.2
* It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
*/
void ConvertOldMultiheadToNew ( void )
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
2006-08-28 18:53:03 +00:00
SETBIT ( v - > subtype , 7 ) ; // indicates that it's the old format and needs to be converted in the next loop
2006-01-18 15:05:01 +00:00
}
}
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_Train ) {
if ( HASBIT ( v - > subtype , 7 ) & & ( ( v - > subtype & ~ 0x80 ) = = 0 | | ( v - > subtype & ~ 0x80 ) = = 4 ) ) {
Vehicle * u = v ;
2006-02-13 21:15:00 +00:00
BEGIN_ENUM_WAGONS ( u ) {
2006-01-18 15:05:01 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( u - > engine_type ) ;
2006-02-13 21:15:00 +00:00
CLRBIT ( u - > subtype , 7 ) ;
switch ( u - > subtype ) {
2006-08-28 18:53:03 +00:00
case 0 : /* TS_Front_Engine */
2006-02-13 21:15:00 +00:00
if ( rvi - > flags & RVI_MULTIHEAD ) SetMultiheaded ( u ) ;
SetFrontEngine ( u ) ;
SetTrainEngine ( u ) ;
2006-01-18 15:05:01 +00:00
break ;
2006-02-13 21:15:00 +00:00
2006-08-28 18:53:03 +00:00
case 1 : /* TS_Artic_Part */
2006-02-13 21:15:00 +00:00
u - > subtype = 0 ;
SetArticulatedPart ( u ) ;
break ;
2006-08-28 18:53:03 +00:00
case 2 : /* TS_Not_First */
2006-02-13 21:15:00 +00:00
u - > subtype = 0 ;
if ( rvi - > flags & RVI_WAGON ) {
// normal wagon
SetTrainWagon ( u ) ;
break ;
}
2006-01-18 15:05:01 +00:00
if ( rvi - > flags & RVI_MULTIHEAD & & rvi - > image_index = = u - > spritenum - 1 ) {
// rear end of a multiheaded engine
SetMultiheaded ( u ) ;
break ;
}
2006-02-13 21:15:00 +00:00
if ( rvi - > flags & RVI_MULTIHEAD ) SetMultiheaded ( u ) ;
2006-01-18 15:05:01 +00:00
SetTrainEngine ( u ) ;
2006-02-13 21:15:00 +00:00
break ;
2006-08-28 18:53:03 +00:00
case 4 : /* TS_Free_Car */
2006-02-13 21:15:00 +00:00
u - > subtype = 0 ;
SetTrainWagon ( u ) ;
SetFreeWagon ( u ) ;
break ;
default : NOT_REACHED ( ) ; break ;
}
} END_ENUM_WAGONS ( u )
2006-01-18 15:05:01 +00:00
}
}
}
}