2005-07-24 14:12:37 +00:00
/* $Id$ */
2007-04-04 03:21:14 +00:00
/** @file train_cmd.cpp */
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"
2007-04-12 13:07:15 +00:00
# include "landscape.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"
2007-07-16 09:16:58 +00:00
# include "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"
2007-06-20 19:17:22 +00:00
# include "timetable.h"
2007-06-01 11:41:02 +00:00
# include "articulated_vehicles.h"
2004-08-09 17:04:08 +00:00
# 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-12-27 12:38:02 +00:00
# include "bridge.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"
2007-02-20 22:09:21 +00:00
# include "cargotype.h"
2007-05-19 09:40:18 +00:00
# include "group.h"
2004-08-09 17:04:08 +00:00
2004-12-21 16:00:14 +00:00
static bool TrainCheckIfLineEnds ( Vehicle * v ) ;
2006-12-05 20:22:14 +00:00
static void TrainController ( Vehicle * v , bool update_image ) ;
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 } ;
2007-02-13 10:46:45 +00:00
static const TrackBits _state_dir_table [ 4 ] = { TRACK_BIT_RIGHT , TRACK_BIT_LOWER , TRACK_BIT_LEFT , TRACK_BIT_UPPER } ;
2004-08-09 17:04:08 +00:00
2006-11-27 23:11:56 +00:00
/** Return the cargo weight multiplier to use for a rail vehicle
2006-12-10 19:00:06 +00:00
* @ param cargo Cargo type to get multiplier for
2006-11-27 23:11:56 +00:00
* @ return Cargo weight multiplier
*/
2006-12-10 19:00:06 +00:00
byte FreightWagonMult ( CargoID cargo )
2006-11-27 23:11:56 +00:00
{
2007-02-22 22:53:49 +00:00
if ( ! GetCargo ( cargo ) - > is_freight ) return 1 ;
2006-11-27 23:11:56 +00:00
return _patches . freight_trains ;
}
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 )
{
2007-04-18 18:37:40 +00:00
uint32 total_power = 0 ;
2006-12-28 13:18:07 +00:00
uint32 max_te = 0 ;
2006-03-29 16:30:26 +00:00
2007-08-30 13:03:56 +00:00
for ( const Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2006-03-29 16:30:26 +00:00
/* Power is not added for articulated parts */
if ( IsArticulatedPart ( u ) ) continue ;
2007-02-27 23:36:28 +00:00
RailType railtype = GetRailType ( u - > tile ) ;
2007-01-21 18:46:19 +00:00
bool engine_has_power = HasPowerOnRail ( u - > u . rail . railtype , railtype ) ;
bool wagon_has_power = HasPowerOnRail ( v - > u . rail . railtype , railtype ) ;
2006-03-29 16:30:26 +00:00
2007-01-21 18:46:19 +00:00
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
2006-03-29 16:30:26 +00:00
2007-04-18 18:37:40 +00:00
if ( engine_has_power ) {
uint16 power = GetVehicleProperty ( u , 0x0B , rvi_u - > power ) ;
if ( power ! = 0 ) {
total_power + = power ;
/* Tractive effort in (tonnes * 1000 * 10 =) N */
max_te + = ( u - > u . rail . cached_veh_weight * 10000 * GetVehicleProperty ( u , 0x1F , rvi_u - > tractive_effort ) ) / 256 ;
}
2006-12-28 13:18:07 +00:00
}
2006-03-29 16:30:26 +00:00
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) & & ( wagon_has_power ) ) {
2007-04-18 18:37:40 +00:00
total_power + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_power ;
2006-03-29 16:30:26 +00:00
}
}
2007-04-18 18:37:40 +00:00
if ( v - > u . rail . cached_power ! = total_power | | v - > u . rail . cached_max_te ! = max_te ) {
2007-09-08 09:52:02 +00:00
/* If it has no power (no catenary), stop the train */
if ( total_power = = 0 ) v - > vehstatus | = VS_STOPPED ;
2007-04-18 18:37:40 +00:00
v - > u . rail . cached_power = total_power ;
2006-12-28 13:18:07 +00:00
v - > u . rail . cached_max_te = max_te ;
2006-03-29 16:30:26 +00:00
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
}
}
2006-12-28 13:18:07 +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 .
*/
static void TrainCargoChanged ( Vehicle * v )
{
uint32 weight = 0 ;
2007-08-30 13:03:56 +00:00
for ( Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2007-06-22 11:58:59 +00:00
uint32 vweight = GetCargo ( u - > cargo_type ) - > weight * u - > cargo . Count ( ) * FreightWagonMult ( u - > cargo_type ) / 16 ;
2006-12-28 13:18:07 +00:00
2007-04-04 03:21:14 +00:00
/* Vehicle weight is not added for articulated parts. */
2006-12-28 13:18:07 +00:00
if ( ! IsArticulatedPart ( u ) ) {
2007-04-04 03:21:14 +00:00
/* vehicle weight is the sum of the weight of the vehicle and the weight of its cargo */
2007-05-04 19:24:01 +00:00
vweight + = GetVehicleProperty ( u , 0x16 , RailVehInfo ( u - > engine_type ) - > weight ) ;
2006-12-28 13:18:07 +00:00
2007-04-04 03:21:14 +00:00
/* powered wagons have extra weight added */
2006-12-28 13:18:07 +00:00
if ( HASBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) )
vweight + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_weight ;
}
2007-04-04 03:21:14 +00:00
/* consist weight is the sum of the weight of all vehicles in the consist */
2006-12-28 13:18:07 +00:00
weight + = vweight ;
2007-04-04 03:21:14 +00:00
/* store vehicle weight in cache */
2006-12-28 13:18:07 +00:00
u - > u . rail . cached_veh_weight = vweight ;
2007-02-25 09:27:03 +00:00
}
2006-12-28 13:18:07 +00:00
2007-04-04 03:21:14 +00:00
/* store consist weight in cache */
2006-12-28 13:18:07 +00:00
v - > u . rail . cached_weight = weight ;
/* Now update train power (tractive effort is dependent on weight) */
TrainPowerChanged ( v ) ;
}
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-05 15:37:00 +00:00
uint16 max_speed = 0xFFFF ;
2005-06-06 14:26:15 +00:00
2007-03-08 16:27:54 +00:00
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
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi_v = RailVehInfo ( v - > engine_type ) ;
EngineID 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
2007-08-30 13:03:56 +00:00
for ( Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2005-06-06 00:19:24 +00:00
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
2007-08-30 21:11:12 +00:00
/* Check the v->first cache. */
assert ( u - > First ( ) = = v ) ;
2006-02-07 18:55:06 +00:00
2007-04-04 03:21:14 +00:00
/* update the 'first engine' */
2007-02-25 09:27:03 +00:00
u - > u . rail . first_engine = v = = u ? INVALID_ENGINE : first_engine ;
2007-01-24 07:14:09 +00:00
u - > u . rail . railtype = rvi_u - > 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 ;
2007-05-10 06:42:43 +00:00
/* Cache wagon override sprite group. NULL is returned if there is none */
u - > u . rail . cached_override = GetWagonOverrideSpriteSet ( u - > engine_type , u - > cargo_type , u - > u . rail . first_engine ) ;
2005-11-04 12:58:18 +00:00
if ( rvi_u - > visual_effect ! = 0 ) {
u - > u . rail . cached_vis_effect = rvi_u - > visual_effect ;
} else {
2005-11-18 23:41:03 +00:00
if ( IsTrainWagon ( u ) | | IsArticulatedPart ( u ) ) {
2007-04-04 03:21:14 +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 ) {
2007-04-04 03:21:14 +00:00
/* Steam is offset by -4 units */
2005-11-04 12:58:18 +00:00
u - > u . rail . cached_vis_effect = 4 ;
} else {
2007-04-04 03:21:14 +00:00
/* Diesel fumes and sparks come from the centre */
2005-11-04 12:58:18 +00:00
u - > u . rail . cached_vis_effect = 8 ;
}
}
2005-11-18 23:41:03 +00:00
if ( ! IsArticulatedPart ( u ) ) {
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 ;
}
2007-01-30 11:53:35 +00:00
if ( rvi_v - > pow_wag_power ! = 0 & & rvi_u - > railveh_type = = RAILVEH_WAGON & &
2007-05-06 20:00:36 +00:00
UsesWagonOverride ( u ) & & ! HASBIT ( u - > u . rail . cached_vis_effect , 7 ) ) {
2007-01-30 11:53:35 +00:00
/* wagon is powered */
SETBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ; // cache 'powered' status
2007-02-25 09:27:03 +00:00
} else {
CLRBIT ( u - > u . rail . flags , VRF_POWEREDWAGON ) ;
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 ;
}
2006-11-17 19:31:44 +00:00
/* Some electric engines can be allowed to run on normal rail. It happens to all
* existing electric engines when elrails are disabled and then re - enabled */
if ( HASBIT ( u - > u . rail . flags , VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL ) ) {
u - > u . rail . railtype = RAILTYPE_RAIL ;
u - > u . rail . compatible_railtypes | = ( 1 < < RAILTYPE_RAIL ) ;
}
2007-04-04 03:21:14 +00:00
/* max speed is the minimum of the speed limits of all vehicles in the consist */
2007-04-18 18:37:40 +00:00
if ( ( rvi_u - > railveh_type ! = RAILVEH_WAGON | | _patches . wagon_speed_limits ) & & ! UsesWagonOverride ( u ) ) {
uint16 speed = GetVehicleProperty ( u , 0x09 , rvi_u - > max_speed ) ;
if ( speed ! = 0 ) max_speed = min ( speed , max_speed ) ;
}
2005-11-05 16:07:26 +00:00
}
2005-06-06 22:44:11 +00:00
2007-05-19 10:29:51 +00:00
if ( u - > cargo_type = = rvi_u - > cargo_type & & u - > cargo_subtype = = 0 ) {
/* Set cargo capacity if we've not been refitted */
u - > cargo_cap = GetVehicleProperty ( u , 0x14 , rvi_u - > capacity ) ;
}
2007-05-12 07:05:34 +00:00
2007-04-04 03:21:14 +00:00
/* check the vehicle length (callback) */
2007-02-25 09:27:03 +00:00
uint16 veh_len = CALLBACK_FAILED ;
2006-06-04 18:22:32 +00:00
if ( HASBIT ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_VEHICLE_LENGTH ) ) {
2007-07-09 19:38:12 +00:00
veh_len = GetVehicleCallback ( CBID_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 ;
2007-08-30 13:03:56 +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 ;
2007-02-25 09:27:03 +00:00
}
2005-06-05 15:37:00 +00:00
2007-04-04 03:21:14 +00:00
/* store consist weight/max speed in cache */
2005-06-05 15:37:00 +00:00
v - > u . rail . cached_max_speed = max_speed ;
2006-03-29 16:30:26 +00:00
2007-04-04 03:21:14 +00:00
/* recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) */
2005-06-06 00:19:24 +00:00
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
2007-03-08 16:27:54 +00:00
assert ( v - > type = = VEH_TRAIN ) ;
2007-04-04 03:21:14 +00:00
/* 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 ;
}
2007-04-04 03:21:14 +00:00
/** new acceleration*/
2005-01-26 12:51:04 +00:00
static int GetTrainAcceleration ( Vehicle * v , bool mode )
{
int max_speed = 2000 ;
2007-08-26 20:43:22 +00:00
int speed = v - > GetDisplaySpeed ( ) ; //[mph]
2005-01-26 12:51:04 +00:00
int curvecount [ 2 ] = { 0 , 0 } ;
2007-04-04 03:21:14 +00:00
/*first find the curve speed limit */
2007-02-25 09:27:03 +00:00
int numcurve = 0 ;
int sum = 0 ;
int pos = 0 ;
int lastpos = - 1 ;
2007-08-30 13:03:56 +00:00
for ( const Vehicle * u = v ; u - > Next ( ) ! = NULL ; u = u - > Next ( ) , pos + + ) {
2006-03-08 06:55:33 +00:00
Direction dir = u - > direction ;
2007-08-30 13:03:56 +00:00
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 ;
}
}
2007-04-04 03:21:14 +00:00
/*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 ) ) {
2007-02-13 16:36:38 +00:00
int station_length = GetStationByTile ( v - > tile ) - > 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 ) ;
}
}
2007-02-25 09:27:03 +00:00
int mass = v - > u . rail . cached_weight ;
int power = v - > u . rail . cached_power * 746 ;
2005-06-05 15:37:00 +00:00
max_speed = min ( max_speed , v - > u . rail . cached_max_speed ) ;
2005-01-26 12:51:04 +00:00
2007-02-25 09:27:03 +00:00
int num = 0 ; //number of vehicles, change this into the number of axles later
int incl = 0 ;
int drag_coeff = 20 ; //[1e-4]
2007-08-30 13:03:56 +00:00
for ( const Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2005-01-26 12:51:04 +00:00
num + + ;
drag_coeff + = 3 ;
2007-02-13 10:46:45 +00:00
if ( u - > u . rail . track = = TRACK_BIT_DEPOT ) 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 ;
2007-02-25 09:27:03 +00:00
const int area = 120 ;
const int friction = 35 ; //[1e-3]
int resistance ;
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]
2006-12-07 14:44:26 +00:00
/* Due to the mph to m/s conversion below, at speeds below 3 mph the force is
* actually double the train ' s power */
2007-02-25 09:27:03 +00:00
const int max_te = v - > u . rail . cached_max_te ; // [N]
int force ;
2006-12-07 14:44:26 +00:00
if ( speed > 2 ) {
2005-01-26 12:51:04 +00:00
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 ;
2006-12-28 13:18:07 +00:00
if ( mode = = AM_ACCEL & & force > max_te ) force = max_te ;
2005-03-09 19:09:04 +00:00
break ;
2007-02-25 09:27:03 +00:00
default : NOT_REACHED ( ) ;
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 {
2007-04-04 03:21:14 +00:00
/* "kickoff" acceleration */
2006-12-28 13:18:07 +00:00
force = ( mode = = AM_ACCEL & & v - > u . rail . railtype ! = RAILTYPE_MAGLEV ) ? min ( max_te , power ) : power ;
force = max ( 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 {
2006-12-07 18:58:20 +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-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
v - > max_speed = v - > u . rail . cached_max_speed ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
uint power = v - > u . rail . cached_power ;
uint weight = v - > u . rail . cached_weight ;
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
}
2007-07-01 19:11:47 +00:00
int Train : : GetImage ( Direction direction ) const
2004-08-09 17:04:08 +00:00
{
2007-07-01 19:11:47 +00:00
int img = this - > spritenum ;
2004-08-09 17:04:08 +00:00
int base ;
2007-07-01 19:11:47 +00:00
if ( HASBIT ( this - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) direction = ReverseDir ( direction ) ;
2006-03-18 13:00:32 +00:00
2004-08-09 17:04:08 +00:00
if ( is_custom_sprite ( img ) ) {
2007-07-01 19:11:47 +00:00
base = GetCustomVehicleSprite ( this , ( Direction ) ( direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE ( img ) ) ) ;
2005-03-09 19:09:04 +00:00
if ( base ! = 0 ) return base ;
2007-07-01 19:11:47 +00:00
img = orig_rail_vehicle_info [ this - > 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 ] ) ;
2007-07-01 19:11:47 +00:00
if ( this - > cargo . Count ( ) > = this - > cargo_cap / 2U ) base + = _wagon_full_adder [ img ] ;
2004-08-09 17:04:08 +00:00
return base ;
}
2007-01-14 19:57:49 +00:00
void DrawTrainEngine ( int x , int y , EngineID engine , SpriteID pal )
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 ;
2007-01-14 19:57:49 +00:00
SpriteID image = 0 ;
2004-08-09 17:04:08 +00:00
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 ] ;
}
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_MULTIHEAD ) {
2007-01-14 19:57:49 +00:00
DrawSprite ( image , pal , x - 14 , y ) ;
2004-08-09 17:04:08 +00:00
x + = 15 ;
image = 0 ;
if ( is_custom_sprite ( img ) ) {
2007-01-10 18:56:51 +00:00
image = GetCustomVehicleIcon ( engine , DIR_E ) ;
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
}
}
2007-01-14 19:57:49 +00:00
DrawSprite ( image , pal , x , y ) ;
2004-08-09 17:04:08 +00:00
}
2007-06-18 10:48:15 +00:00
static CommandCost CmdBuildRailWagon ( EngineID engine , TileIndex tile , uint32 flags )
2004-08-09 17:04:08 +00:00
{
2005-01-04 17:11:03 +00:00
SET_EXPENSES_TYPE ( EXPENSES_NEW_VEHICLES ) ;
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( engine ) ;
2007-06-18 19:53:50 +00:00
CommandCost value ( ( GetEngineProperty ( engine , 0x17 , rvi - > base_cost ) * _price . build_railwagon ) > > 8 ) ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
uint 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.
2004-08-09 17:04:08 +00:00
2006-10-12 15:03:19 +00:00
memset ( & vl , 0 , sizeof ( vl ) ) ;
2007-08-03 19:36:00 +00:00
if ( ! Vehicle : : AllocateList ( vl , num_vehicles ) )
2005-11-05 16:07:26 +00:00
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
2004-08-09 17:04:08 +00:00
if ( flags & DC_EXEC ) {
2007-02-25 09:27:03 +00:00
Vehicle * v = vl [ 0 ] ;
2005-11-14 19:48:04 +00:00
v - > spritenum = rvi - > image_index ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * u = NULL ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * w ;
2005-01-06 22:31:58 +00:00
FOR_ALL_VEHICLES ( w ) {
2007-03-08 16:27:54 +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 ;
2007-02-25 09:27:03 +00:00
DiagDirection 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
2007-02-25 09:27:03 +00:00
int x = TileX ( tile ) * TILE_SIZE | _vehicle_initial_x_fract [ dir ] ;
int 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 ;
2007-02-25 09:27:03 +00:00
v - > z_pos = GetSlopeZ ( x , y ) ;
2004-08-09 17:04:08 +00:00
v - > owner = _current_player ;
2007-02-13 10:46:45 +00:00
v - > u . rail . track = TRACK_BIT_DEPOT ;
2004-08-09 17:04:08 +00:00
v - > vehstatus = VS_HIDDEN | VS_DEFPAL ;
2007-06-01 11:17:30 +00:00
v = new ( v ) Train ( ) ;
2005-11-18 23:41:03 +00:00
v - > subtype = 0 ;
SetTrainWagon ( v ) ;
2007-06-01 11:17:30 +00:00
2004-08-09 17:04:08 +00:00
if ( u ! = NULL ) {
2007-08-30 13:09:44 +00:00
u - > SetNext ( v ) ;
2005-11-18 23:41:03 +00:00
} else {
SetFreeWagon ( v ) ;
2006-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
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 ;
2007-06-18 19:53:50 +00:00
v - > value = value . GetCost ( ) ;
2004-08-09 17:04:08 +00:00
// v->day_counter = 0;
2007-01-24 07:14:09 +00:00
v - > u . rail . railtype = rvi - > 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 - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
v - > random_bits = VehicleRandomBits ( ) ;
2004-09-10 19:02:27 +00:00
2007-05-19 09:40:18 +00:00
v - > group_id = DEFAULT_GROUP ;
2007-06-11 14:00:16 +00:00
AddArticulatedParts ( vl , VEH_TRAIN ) ;
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 ) ;
2007-08-30 21:11:12 +00:00
TrainConsistChanged ( v - > First ( ) ) ;
UpdateTrainGroupID ( v - > First ( ) ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2006-02-04 18:25:07 +00:00
if ( IsLocalPlayer ( ) ) {
2007-08-07 23:07:10 +00:00
InvalidateAutoreplaceWindow ( v - > engine_type , v - > group_id ) ; // updates the replace Train window
2006-02-04 18:25:07 +00:00
}
2007-02-06 11:11:12 +00:00
GetPlayer ( _current_player ) - > num_engines [ engine ] + + ;
2004-08-09 17:04:08 +00:00
}
}
2007-06-18 19:53:50 +00:00
return CommandCost ( value ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 03:21:14 +00:00
/** 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 ) {
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN & & IsFreeWagon ( v ) & &
2004-08-09 17:04:08 +00:00
v - > tile = = u - > tile & &
2007-02-13 10:46:45 +00:00
v - > u . rail . track = = TRACK_BIT_DEPOT ) {
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 ;
}
}
}
2007-06-18 10:48:15 +00:00
static CommandCost EstimateTrainCost ( EngineID engine , const RailVehicleInfo * rvi )
2004-08-09 17:04:08 +00:00
{
2007-06-18 19:53:50 +00:00
return CommandCost ( GetEngineProperty ( engine , 0x17 , 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
{
2007-08-30 21:11:12 +00:00
u = new ( u ) Train ( ) ;
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 ;
2007-02-13 10:46:45 +00:00
u - > u . rail . track = TRACK_BIT_DEPOT ;
2005-01-18 23:27:06 +00:00
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 ;
2007-08-30 13:09:44 +00:00
if ( building ) v - > SetNext ( u ) ;
2005-01-18 23:27:06 +00:00
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 - > 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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdBuildRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
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) */
2007-04-02 11:35:08 +00:00
if ( ! IsEngineBuildable ( p1 , VEH_TRAIN , _current_player ) ) return_cmd_error ( STR_RAIL_VEHICLE_NOT_AVAILABLE ) ;
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 ) ;
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( p1 ) ;
2005-07-31 13:08:08 +00:00
/* 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 */
2007-01-24 07:14:09 +00:00
if ( ! HasPowerOnRail ( rvi - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
2004-09-10 19:02:27 +00:00
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_WAGON ) return CmdBuildRailWagon ( p1 , tile , flags ) ;
2004-08-09 17:04:08 +00:00
2007-06-18 10:48:15 +00:00
CommandCost value = EstimateTrainCost ( p1 , rvi ) ;
2005-08-01 16:31:19 +00:00
2007-02-25 09:27:03 +00:00
uint num_vehicles =
( rvi - > railveh_type = = RAILVEH_MULTIHEAD ? 2 : 1 ) +
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
2006-10-12 15:03:19 +00:00
memset ( & vl , 0 , sizeof ( vl ) ) ;
2007-08-03 19:36:00 +00:00
if ( ! Vehicle : : AllocateList ( vl , num_vehicles ) )
2004-08-09 17:04:08 +00:00
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
2007-02-25 09:27:03 +00:00
Vehicle * v = vl [ 0 ] ;
2005-11-05 16:07:26 +00:00
2007-03-08 16:27:54 +00:00
UnitID 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 ;
2007-02-25 09:27:03 +00:00
v - > z_pos = GetSlopeZ ( x , y ) ;
2007-02-13 10:46:45 +00:00
v - > u . rail . track = TRACK_BIT_DEPOT ;
2004-08-09 17:04:08 +00:00
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 ;
2007-06-18 19:53:50 +00:00
v - > value = value . GetCost ( ) ;
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
2007-01-24 07:14:09 +00:00
const Engine * e = GetEngine ( 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 ;
2007-01-24 07:14:09 +00:00
v - > u . rail . railtype = rvi - > 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 ;
2007-04-29 21:24:08 +00:00
v = new ( v ) Train ( ) ;
2004-08-09 17:04:08 +00:00
v - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
v - > random_bits = VehicleRandomBits ( ) ;
2004-08-09 17:04:08 +00:00
2007-02-28 17:59:05 +00:00
v - > vehicle_flags = 0 ;
if ( e - > flags & ENGINE_EXCLUSIVE_PREVIEW ) SETBIT ( v - > vehicle_flags , VF_BUILT_AS_PROTOTYPE ) ;
2007-05-19 09:40:18 +00:00
v - > group_id = DEFAULT_GROUP ;
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 ) ;
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_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 {
2007-06-11 14:00:16 +00:00
AddArticulatedParts ( vl , VEH_TRAIN ) ;
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 ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( 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-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
2004-12-10 18:16:08 +00:00
RebuildVehicleLists ( ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
2007-02-06 11:11:12 +00:00
if ( IsLocalPlayer ( ) )
2007-08-07 23:07:10 +00:00
InvalidateAutoreplaceWindow ( v - > engine_type , v - > group_id ) ; // updates the replace Train window
2007-02-06 11:11:12 +00:00
GetPlayer ( _current_player ) - > num_engines [ p1 ] + + ;
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
{
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
2007-02-25 09:27:03 +00:00
int count = 0 ;
2007-08-30 13:03:56 +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 */
2007-09-05 10:15:23 +00:00
if ( ! IsArticulatedPart ( v ) & & ! IsRearDualheaded ( v ) ) count + + ;
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track ! = TRACK_BIT_DEPOT | | 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 */
2007-01-10 18:56:51 +00:00
int CheckTrainStoppedInDepot ( const Vehicle * v )
2006-09-03 11:49:38 +00:00
{
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 )
{
2007-02-25 09:27:03 +00:00
return CheckTrainInDepot ( v , false ) > 0 ;
2006-09-03 11:49:38 +00:00
}
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 )
{
2007-04-04 03:21:14 +00:00
/* unlinking the first vehicle of the chain? */
2004-08-09 17:04:08 +00:00
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
2007-02-25 09:27:03 +00:00
Vehicle * u ;
2005-11-05 16:07:26 +00:00
for ( u = first ; GetNextVehicle ( u ) ! = v ; u = GetNextVehicle ( u ) ) { }
2007-08-30 13:09:44 +00:00
GetLastEnginePart ( u ) - > SetNext ( 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 ) {
2007-03-08 16:27:54 +00:00
if ( dst - > type = = VEH_TRAIN & & IsFreeWagon ( dst ) & & dst - > tile = = tile ) {
2007-04-04 03:21:14 +00:00
/* check so all vehicles in the line have the same engine. */
2004-08-09 17:04:08 +00:00
Vehicle * v = dst ;
2005-03-09 19:09:04 +00:00
2004-08-09 17:04:08 +00:00
while ( v - > engine_type = = eng ) {
2007-08-30 13:03:56 +00:00
v = v - > Next ( ) ;
2005-03-09 19:09:04 +00:00
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 )
{
2007-08-30 21:11:12 +00:00
UnlinkWagon ( v , v - > First ( ) ) ;
2005-11-18 23:41:03 +00:00
if ( dest = = NULL ) return ;
2007-08-30 21:11:12 +00:00
Vehicle * next = dest - > Next ( ) ;
2007-08-30 13:03:56 +00:00
dest - > SetNext ( v ) ;
2007-08-30 21:11:12 +00:00
v - > SetNext ( next ) ;
2005-11-18 23:41:03 +00:00
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 )
{
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 */
2007-02-25 09:27:03 +00:00
Vehicle * u ;
2007-08-30 13:03:56 +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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
*/
2007-06-18 10:48:15 +00:00
CommandCost 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
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( s ) ) return CMD_ERROR ;
2005-01-30 20:50:06 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * src = GetVehicle ( s ) ;
2005-01-30 20:50:06 +00:00
2007-03-08 16:27:54 +00:00
if ( src - > type ! = VEH_TRAIN | | ! CheckOwnership ( src - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* if nothing is selected as destination, try and find a matching vehicle to drag to. */
2007-02-25 09:27:03 +00:00
Vehicle * dst ;
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 {
2007-02-24 14:36:14 +00:00
if ( ! IsValidVehicleID ( d ) ) return CMD_ERROR ;
2005-10-03 21:20:01 +00:00
dst = GetVehicle ( d ) ;
2007-03-08 16:27:54 +00:00
if ( dst - > type ! = VEH_TRAIN | | ! CheckOwnership ( dst - > owner ) ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 03:21:14 +00:00
/* if an articulated part is being handled, deal with its parent vehicle */
2007-08-30 21:11:12 +00:00
while ( IsArticulatedPart ( src ) ) src = src - > Previous ( ) ;
2005-11-05 16:07:26 +00:00
if ( dst ! = NULL ) {
2007-08-30 21:11:12 +00:00
while ( IsArticulatedPart ( dst ) ) dst = dst - > Previous ( ) ;
2005-11-05 16:07:26 +00:00
}
2007-04-04 03:21:14 +00:00
/* don't move the same vehicle.. */
2007-06-18 19:53:50 +00:00
if ( src = = dst ) return CommandCost ( ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* locate the head of the two chains */
2007-08-30 21:11:12 +00:00
Vehicle * src_head = src - > First ( ) ;
2007-02-25 09:27:03 +00:00
Vehicle * dst_head ;
2005-11-05 16:07:26 +00:00
if ( dst ! = NULL ) {
2007-08-30 21:11:12 +00:00
dst_head = dst - > First ( ) ;
2007-02-24 14:36:14 +00:00
if ( dst_head - > tile ! = src_head - > tile ) return CMD_ERROR ;
2007-04-04 03:21:14 +00:00
/* Now deal with articulated part of destination wagon */
2005-11-05 16:07:26 +00:00
dst = GetLastEnginePart ( dst ) ;
2007-02-24 14:36:14 +00:00
} else {
dst_head = NULL ;
2005-11-05 16:07:26 +00:00
}
2004-09-10 19:02:27 +00:00
2007-09-05 10:15:23 +00:00
if ( IsRearDualheaded ( src ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
2005-11-18 23:41:03 +00:00
2007-04-04 03:21:14 +00:00
/* when moving all wagons, we can't have the same src_head and dst_head */
2007-06-18 19:53:50 +00:00
if ( HASBIT ( p2 , 0 ) & & src_head = = dst_head ) return CommandCost ( ) ;
2006-01-25 21:33:57 +00:00
2006-01-06 21:10:58 +00:00
{
2006-01-25 21:33:57 +00:00
int max_len = _patches . mammoth_trains ? 100 : 9 ;
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* check if all vehicles in the source train are stopped inside a depot. */
2007-02-25 09:27:03 +00:00
int 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
2007-04-04 03:21:14 +00:00
/* check the destination row if the source and destination aren't the same. */
2006-01-25 21:33:57 +00:00
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 ) {
2007-04-04 03:21:14 +00:00
/* check if all vehicles in the dest train are stopped. */
2006-01-25 21:33:57 +00:00
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-25 21:33:57 +00:00
}
2006-01-06 21:10:58 +00:00
2007-04-04 03:21:14 +00:00
/* We are moving between rows, so only count the wagons from the source
* row that are being moved . */
2006-01-25 21:33:57 +00:00
if ( HASBIT ( p2 , 0 ) ) {
const Vehicle * u ;
for ( u = src_head ; u ! = src & & u ! = NULL ; u = GetNextVehicle ( u ) )
src_len - - ;
} else {
2007-04-04 03:21:14 +00:00
/* If moving only one vehicle, just count that. */
2006-01-25 21:33:57 +00:00
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 ) {
2007-04-04 03:21:14 +00:00
/* Abort if we're adding too many wagons to a train. */
2006-01-25 21:33:57 +00:00
if ( dst_head ! = NULL & & IsFrontEngine ( dst_head ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
2007-04-04 03:21:14 +00:00
/* Abort if we're making a train on a new row. */
2006-01-25 21:33:57 +00:00
if ( dst_head = = NULL & & IsTrainEngine ( src ) ) return_cmd_error ( STR_8819_TRAIN_TOO_LONG ) ;
}
} else {
2007-04-04 03:21:14 +00:00
/* Abort if we're creating a new train on an existing row. */
2006-01-25 21:33:57 +00:00
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
}
2007-04-04 03:21:14 +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 ) ) {
2007-03-08 16:27:54 +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 ) {
2007-05-19 09:40:18 +00:00
/* If we move the front Engine and if the second vehicle is not an engine
add the whole vehicle to the DEFAULT_GROUP */
if ( IsFrontEngine ( src ) & & ! IsDefaultGroupID ( src - > group_id ) ) {
2007-09-05 21:05:12 +00:00
Vehicle * v = GetNextVehicle ( src ) ;
2007-05-19 09:40:18 +00:00
2007-09-05 21:05:12 +00:00
if ( v ! = NULL & & IsTrainEngine ( v ) ) {
v - > group_id = src - > group_id ;
src - > group_id = DEFAULT_GROUP ;
2007-05-19 09:40:18 +00:00
}
}
2005-05-09 22:33:00 +00:00
if ( HASBIT ( p2 , 0 ) ) {
2007-04-04 03:21:14 +00:00
/* unlink ALL wagons */
2004-08-09 17:04:08 +00:00
if ( src ! = src_head ) {
Vehicle * v = src_head ;
2005-11-05 16:07:26 +00:00
while ( GetNextVehicle ( v ) ! = src ) v = GetNextVehicle ( v ) ;
2007-08-30 13:09:44 +00:00
GetLastEnginePart ( v ) - > SetNext ( NULL ) ;
2005-06-06 14:26:15 +00:00
} else {
2006-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , src_head - > tile ) ; // We removed a line
2005-06-06 14:26:15 +00:00
src_head = NULL ;
2004-08-09 17:04:08 +00:00
}
} else {
2007-04-04 03:21:14 +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 ;
2007-04-04 03:21:14 +00:00
/* unlink single wagon from linked list */
2005-06-06 14:26:15 +00:00
src_head = UnlinkWagon ( src , src_head ) ;
2007-08-30 13:09:44 +00:00
GetLastEnginePart ( src ) - > SetNext ( NULL ) ;
2004-08-09 17:04:08 +00:00
}
if ( dst = = NULL ) {
2006-10-05 12:59:28 +00:00
/* We make a new line in the depot, so we know already that we invalidate the window data */
2006-10-10 17:53:08 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , src - > tile ) ;
2006-10-05 12:59:28 +00:00
2007-04-04 03:21:14 +00:00
/* move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4. */
2005-11-18 23:41:03 +00:00
if ( IsTrainEngine ( src ) ) {
if ( ! IsFrontEngine ( src ) ) {
2007-04-04 03:21:14 +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 ;
2007-05-19 09:40:18 +00:00
// Decrease the engines number of the src engine_type
if ( ! IsDefaultGroupID ( src - > group_id ) & & IsValidGroupID ( src - > group_id ) ) {
GetGroup ( src - > group_id ) - > num_engines [ src - > engine_type ] - - ;
}
// If we move an engine to a new line affect it to the DEFAULT_GROUP
src - > group_id = DEFAULT_GROUP ;
2004-08-09 17:04:08 +00:00
}
} 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 ) ) {
2007-04-04 03:21:14 +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 ) ;
2007-09-05 21:05:12 +00:00
RemoveVehicleFromGroup ( src ) ;
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2006-10-05 12:59:28 +00:00
if ( IsFrontEngine ( src ) | | IsFreeWagon ( src ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , src - > tile ) ;
ClearFrontEngine ( src ) ;
ClearFreeWagon ( src ) ;
src - > unitnumber = 0 ; // doesn't occupy a unitnumber anymore.
}
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* link in the wagon(s) in the chain. */
2004-08-09 17:04:08 +00:00
{
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 ) ) ;
2007-08-30 13:03:56 +00:00
GetLastEnginePart ( v ) - > SetNext ( dst - > Next ( ) ) ;
2004-08-09 17:04:08 +00:00
}
2007-08-30 13:03:56 +00:00
dst - > SetNext ( src ) ;
2004-08-09 17:04:08 +00:00
}
2007-08-30 21:11:12 +00:00
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 ) {
2007-08-30 13:03:56 +00:00
src_head = src_head - > Next ( ) ;
2005-11-18 23:41:03 +00:00
}
AddWagonToConsist ( src - > u . rail . other_multiheaded_part , src ) ;
}
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 ) ) {
2007-05-19 09:40:18 +00:00
/* As in CmdMoveRailVehicle src_head->group_id will be equal to DEFAULT_GROUP
* we need to save the group and reaffect it to src_head */
const GroupID tmp_g = src_head - > group_id ;
2006-04-10 07:15:58 +00:00
CmdMoveRailVehicle ( 0 , flags , src_head - > index | ( INVALID_VEHICLE < < 16 ) , 1 ) ;
2007-05-19 09:40:18 +00:00
SetTrainGroupID ( src_head , tmp_g ) ;
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 ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( 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 ) ;
2007-02-25 09:27:03 +00:00
}
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 ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( 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
}
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
2005-05-09 22:33:00 +00:00
/** Start/Stop a train.
2006-04-10 07:15:58 +00:00
* @ param tile unused
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
2005-05-09 22:33:00 +00:00
* @ param p1 train to start / stop
* @ param p2 unused
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdStartStopTrain ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
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
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2007-03-08 16:27:54 +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 . */
2007-02-25 09:27:03 +00:00
uint16 callback = GetVehicleCallback ( CBID_VEHICLE_START_STOP_CHECK , 0 , 0 , v - > engine_type , v ) ;
2006-08-09 21:11:45 +00:00
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 ) {
2007-02-13 10:46:45 +00:00
if ( v - > vehstatus & VS_STOPPED & & v - > u . rail . track = = TRACK_BIT_DEPOT ) {
2006-03-04 11:01:35 +00:00
DeleteVehicleNews ( p1 , STR_8814_TRAIN_IS_WAITING_IN_DEPOT ) ;
}
2004-08-09 17:04:08 +00:00
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 ) ;
}
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdSellRailWagon ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
2004-08-09 17:04:08 +00:00
{
2007-06-02 20:08:23 +00:00
/* Check if we deleted a vehicle window */
Window * w = NULL ;
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( p1 ) | | p2 > 2 ) return CMD_ERROR ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2007-03-08 16:27:54 +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 ) ;
2007-08-30 21:11:12 +00:00
while ( IsArticulatedPart ( v ) ) v = v - > Previous ( ) ;
Vehicle * first = v - > First ( ) ;
2004-09-10 19:02:27 +00:00
2007-04-04 03:21:14 +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
2007-09-05 10:15:23 +00:00
if ( IsRearDualheaded ( v ) ) return_cmd_error ( STR_REAR_ENGINE_FOLLOW_FRONT_ERROR ) ;
2005-11-18 23:41:03 +00:00
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 ) ) {
2007-06-02 20:08:23 +00:00
w = FindWindowById ( WC_VEHICLE_VIEW , first - > index ) ;
if ( w ! = NULL ) DeleteWindow ( w ) ;
2005-11-08 23:18:09 +00:00
}
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
2007-06-18 19:53:50 +00:00
CommandCost cost ;
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 ) {
2007-06-21 14:32:27 +00:00
cost . AddCost ( - 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 ) ;
2006-12-05 22:59:42 +00:00
DeleteDepotHighlightOfVehicle ( rear ) ;
2007-08-03 19:36:00 +00:00
delete rear ;
2005-05-05 20:46:14 +00:00
}
}
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 ) {
2007-02-25 09:27:03 +00:00
Vehicle * new_f = GetNextVehicle ( first ) ;
2005-05-05 20:46:14 +00:00
/* 2.2 If there are wagons present after the deleted front engine, check
2007-03-18 15:32:42 +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 */
if ( new_f ! = NULL & & IsTrainEngine ( new_f ) ) {
switch_engine = true ;
/* Copy important data from the front engine */
new_f - > unitnumber = first - > unitnumber ;
new_f - > current_order = first - > current_order ;
new_f - > cur_order_index = first - > cur_order_index ;
new_f - > orders = first - > orders ;
new_f - > num_orders = first - > num_orders ;
2007-09-05 21:05:12 +00:00
new_f - > group_id = first - > group_id ;
2007-03-18 15:32:42 +00:00
if ( first - > prev_shared ! = NULL ) {
first - > prev_shared - > next_shared = new_f ;
new_f - > prev_shared = first - > prev_shared ;
2005-05-05 20:46:14 +00:00
}
2007-03-18 15:32:42 +00:00
if ( first - > next_shared ! = NULL ) {
first - > next_shared - > prev_shared = new_f ;
new_f - > next_shared = first - > next_shared ;
}
/*
* Remove all order information from the front train , to
* prevent the order and the shared order list to be
* destroyed by Destroy / DeleteVehicle .
*/
first - > orders = NULL ;
first - > prev_shared = NULL ;
first - > next_shared = NULL ;
2007-09-05 21:05:12 +00:00
first - > group_id = DEFAULT_GROUP ;
2007-03-18 15:32:42 +00:00
2007-06-02 20:08:23 +00:00
/* If we deleted a window then open a new one for the 'new' train */
2007-08-29 20:50:58 +00:00
if ( IsLocalPlayer ( ) & & w ! = NULL ) ShowVehicleViewWindow ( new_f ) ;
2005-05-05 20:46:14 +00:00
}
}
/* 3. Delete the requested wagon */
2007-06-18 19:53:50 +00:00
cost . AddCost ( - v - > value ) ;
2005-05-05 20:46:14 +00:00
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
2006-12-05 22:59:42 +00:00
DeleteDepotHighlightOfVehicle ( v ) ;
2007-08-03 19:36:00 +00:00
delete 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 ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( 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 ) ) {
2007-02-25 09:27:03 +00:00
Vehicle * tmp ;
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 */
2007-02-25 09:27:03 +00:00
Vehicle * tmp ;
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 ) {
2007-06-18 19:53:50 +00:00
cost . AddCost ( - rear - > value ) ;
2007-06-02 14:42:25 +00:00
/* If this is a multiheaded vehicle with nothing
* between the parts , tmp will be pointing to the
* rear part , which is unlinked from the train and
* deleted here . However , because tmp has already
* been set it needs to be updated now so that the
* loop never sees the rear part . */
if ( tmp = = rear ) tmp = GetNextVehicle ( tmp ) ;
2005-11-18 23:41:03 +00:00
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( rear , first ) ;
2006-12-05 22:59:42 +00:00
DeleteDepotHighlightOfVehicle ( rear ) ;
2007-08-03 19:36:00 +00:00
delete rear ;
2005-11-18 23:41:03 +00:00
}
}
} 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
2007-06-18 19:53:50 +00:00
cost . AddCost ( - v - > value ) ;
2005-05-05 20:46:14 +00:00
if ( flags & DC_EXEC ) {
first = UnlinkWagon ( v , first ) ;
2006-12-05 22:59:42 +00:00
DeleteDepotHighlightOfVehicle ( v ) ;
2007-05-19 09:40:18 +00:00
RemoveVehicleFromGroup ( v ) ;
2007-09-05 21:05:12 +00:00
delete v ;
2005-05-05 20:46:14 +00:00
}
}
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 ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( 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 ;
}
2007-05-01 16:35:14 +00:00
void Train : : UpdateDeltaXY ( Direction direction )
2004-08-09 17:04:08 +00:00
{
2007-05-01 16:35:14 +00:00
# define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
2004-08-09 17:04:08 +00:00
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 ] ;
2007-05-01 16:35:14 +00:00
this - > x_offs = GB ( x , 0 , 8 ) ;
this - > y_offs = GB ( x , 8 , 8 ) ;
this - > sprite_width = GB ( x , 16 , 8 ) ;
this - > sprite_height = GB ( x , 24 , 8 ) ;
this - > z_height = 6 ;
2004-08-09 17:04:08 +00:00
}
static void UpdateVarsAfterSwap ( Vehicle * v )
{
2007-05-01 16:35:14 +00:00
v - > UpdateDeltaXY ( v - > direction ) ;
2007-07-01 19:11:47 +00:00
v - > cur_image = v - > GetImage ( v - > direction ) ;
2004-08-09 17:04:08 +00:00
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 )
{
2007-02-25 09:27:03 +00:00
byte flag1 = * swap_flag1 ;
byte flag2 = * swap_flag2 ;
2005-01-09 16:02:06 +00:00
/* 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 */
2007-08-30 13:03:56 +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
2007-01-19 11:47:48 +00:00
Swap ( a - > u . rail . track , b - > u . rail . track ) ;
Swap ( a - > direction , b - > direction ) ;
2004-08-09 17:04:08 +00:00
/* toggle direction */
2007-02-13 10:46:45 +00:00
if ( a - > u . rail . track ! = TRACK_BIT_DEPOT ) a - > direction = ReverseDir ( a - > direction ) ;
if ( b - > u . rail . track ! = TRACK_BIT_DEPOT ) b - > direction = ReverseDir ( b - > direction ) ;
2004-09-10 19:02:27 +00:00
2007-01-19 11:47:48 +00:00
Swap ( a - > x_pos , b - > x_pos ) ;
Swap ( a - > y_pos , b - > y_pos ) ;
Swap ( a - > tile , b - > tile ) ;
Swap ( a - > z_pos , b - > z_pos ) ;
2004-08-09 17:04:08 +00:00
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-12-27 12:38:02 +00:00
/* call the proper EnterTile function unless we are in a wormhole */
2007-02-13 10:46:45 +00:00
if ( a - > u . rail . track ! = TRACK_BIT_WORMHOLE ) VehicleEnterTile ( a , a - > tile , a - > x_pos , a - > y_pos ) ;
if ( b - > u . rail . track ! = TRACK_BIT_WORMHOLE ) VehicleEnterTile ( b , b - > tile , b - > x_pos , b - > y_pos ) ;
2004-08-09 17:04:08 +00:00
} else {
2007-02-13 10:46:45 +00:00
if ( a - > u . rail . track ! = TRACK_BIT_DEPOT ) a - > direction = ReverseDir ( a - > direction ) ;
2004-09-10 19:02:27 +00:00
UpdateVarsAfterSwap ( a ) ;
2005-03-25 12:07:26 +00:00
2007-02-13 10:46:45 +00:00
if ( a - > u . rail . track ! = TRACK_BIT_WORMHOLE ) 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 )
{
2007-03-08 16:27:54 +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 )
{
2007-02-25 09:27:03 +00:00
Vehicle * base = v ;
2007-08-30 13:03:56 +00:00
Vehicle * first = base - > Next ( ) ;
2007-02-25 09:27:03 +00:00
uint length = CountVehiclesInChain ( v ) ;
2005-06-06 22:44:11 +00:00
while ( length > 2 ) {
2007-04-04 03:21:14 +00:00
/* find pairwise matching wagon
* start < > end , start + 1 < > end - 1 , . . . */
2007-02-25 09:27:03 +00:00
Vehicle * last = first ;
2007-08-30 13:03:56 +00:00
for ( uint i = length - 3 ; i > 0 ; i - - ) last = last - > Next ( ) ;
2005-06-06 22:44:11 +00:00
2007-02-25 09:27:03 +00:00
int 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 ) {
2007-04-04 03:21:14 +00:00
/* disconnect last car to make sure only this subset moves */
2007-08-30 13:03:56 +00:00
Vehicle * tempnext = last - > Next ( ) ;
last - > SetNext ( NULL ) ;
2005-06-06 22:44:11 +00:00
2007-02-25 09:27:03 +00:00
for ( int i = 0 ; i < differential ; i + + ) TrainController ( first , false ) ;
2005-06-06 22:44:11 +00:00
2007-08-30 13:03:56 +00:00
last - > SetNext ( tempnext ) ;
2005-06-06 22:44:11 +00:00
}
base = first ;
2007-08-30 13:03:56 +00:00
first = first - > Next ( ) ;
2005-06-06 22:44:11 +00:00
length - = 2 ;
}
}
2006-02-01 06:32:03 +00:00
2004-08-09 17:04:08 +00:00
static void ReverseTrainDirection ( Vehicle * v )
{
2006-10-05 12:59:28 +00:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 17:04:08 +00:00
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
}
2007-04-04 03:21:14 +00:00
/* count number of vehicles */
2007-02-25 09:27:03 +00:00
int r = - 1 ;
const Vehicle * u = v ;
2007-08-30 13:03:56 +00:00
do r + + ; while ( ( u = u - > Next ( ) ) ! = NULL ) ;
2004-08-09 17:04:08 +00:00
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, ... */
2007-02-25 09:27:03 +00:00
int l = 0 ;
2004-08-09 17:04:08 +00:00
do {
ReverseTrainSwapVeh ( v , l + + , r - - ) ;
} while ( l < = r ) ;
2005-06-06 22:44:11 +00:00
AdvanceWagons ( v , false ) ;
2006-10-05 12:59:28 +00:00
if ( IsTileDepotType ( v - > tile , TRANSPORT_RAIL ) ) {
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 17:04:08 +00:00
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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdReverseTrainDirection ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
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
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2007-03-08 16:27:54 +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 ) {
2007-04-04 03:21:14 +00:00
/* turn a single unit around */
2006-03-18 13:00:32 +00:00
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 ) ;
}
2007-08-30 21:11:12 +00:00
Vehicle * front = v - > First ( ) ;
2007-04-04 03:21:14 +00:00
/* make sure the vehicle is stopped in the depot */
2006-03-18 13:00:32 +00:00
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-10-15 20:46:10 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
2006-03-18 13:15:34 +00:00
}
} else {
2007-08-26 20:16:02 +00:00
/* turn the whole train around */
if ( v - > vehstatus & VS_CRASHED | | v - > breakdown_ctr ! = 0 ) return CMD_ERROR ;
2006-03-18 13:15:34 +00:00
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
}
}
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
2005-05-09 22:33:00 +00:00
* @ param p1 train to ignore the red signal
* @ param p2 unused
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdForceTrainProceed ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
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
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2004-08-09 17:04:08 +00:00
2007-03-08 16:27:54 +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
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
2007-04-29 08:43:00 +00:00
* - p2 = ( bit 16 ) - refit only this vehicle
* @ return cost of refit or error
2005-05-14 12:36:16 +00:00
*/
2007-06-18 10:48:15 +00:00
CommandCost 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 ) ;
2007-04-29 08:43:00 +00:00
bool only_this = HASBIT ( p2 , 16 ) ;
2004-08-09 17:04:08 +00:00
2006-08-22 18:15:17 +00:00
if ( ! IsValidVehicleID ( p1 ) ) return CMD_ERROR ;
2005-01-06 18:45:28 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2005-01-30 20:50:06 +00:00
2007-03-08 16:27:54 +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 */
2007-03-16 21:42:11 +00:00
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 ) ;
2007-06-18 19:53:50 +00:00
CommandCost cost ;
2007-02-25 09:27:03 +00:00
uint 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-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
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
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 ;
}
2007-02-25 09:27:03 +00:00
}
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 ) {
2007-06-18 19:53:50 +00:00
cost . AddCost ( 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 ) {
2007-06-22 11:58:59 +00:00
v - > cargo . Truncate ( ( v - > cargo_type = = new_cid ) ? amount : 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
}
2007-08-30 13:03:56 +00:00
} while ( ( v = v - > Next ( ) ) ! = NULL & & ! only_this ) ;
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 */
2007-08-30 21:11:12 +00:00
if ( flags & DC_EXEC ) TrainConsistChanged ( GetVehicle ( p1 ) - > First ( ) ) ;
2006-10-02 22:10:04 +00:00
2004-08-09 17:04:08 +00:00
return cost ;
}
2007-03-07 12:11:48 +00:00
struct TrainFindDepotData {
2004-08-09 17:04:08 +00:00
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 ;
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
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
}
2007-04-04 03:21:14 +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
{
2005-05-03 20:45:23 +00:00
assert ( ! ( v - > vehstatus & VS_CRASHED ) ) ;
2007-02-25 09:27:03 +00:00
TrainFindDepotData tfdd ;
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
2007-02-25 09:27:03 +00:00
TileIndex tile = v - > tile ;
if ( IsTileDepotType ( tile , TRANSPORT_RAIL ) ) {
2004-09-23 21:20:38 +00:00
tfdd . tile = tile ;
tfdd . best_length = 0 ;
return tfdd ;
}
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-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
2007-02-25 09:27:03 +00:00
assert ( trackdir ! = INVALID_TRACKDIR ) ;
2007-05-24 22:41:50 +00:00
NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , TRANSPORT_RAIL , 0 , 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 {
2007-04-04 03:21:14 +00:00
/* search in the forward direction first. */
2007-02-25 09:27:03 +00:00
DiagDirection 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 ;
2007-04-04 03:21:14 +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
2007-04-04 03:21:14 +00:00
* @ param flags type of operation
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
*/
2007-06-18 10:48:15 +00:00
CommandCost CmdSendTrainToDepot ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 )
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 ;
2007-03-08 16:27:54 +00:00
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
2007-02-25 09:27:03 +00:00
Vehicle * v = GetVehicle ( p1 ) ;
2005-01-30 20:50:06 +00:00
2007-03-08 16:27:54 +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 ) ;
}
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2006-09-01 12:37:03 +00:00
}
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 - > 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
}
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
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 ;
2007-02-25 09:27:03 +00:00
TrainFindDepotData 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 ) {
2007-04-20 11:39:01 +00:00
if ( v - > current_order . type = = OT_LOADING ) v - > LeaveStation ( ) ;
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 ;
2006-12-05 23:57:04 +00:00
v - > current_order . refit_cargo = CT_INVALID ;
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 */
2007-04-20 11:39:01 +00:00
if ( tfdd . reverse ) 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
2007-06-18 19:53:50 +00:00
return CommandCost ( ) ;
2004-08-09 17:04:08 +00:00
}
2007-03-07 11:47:46 +00:00
void OnTick_Train ( )
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
{
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 ;
2007-02-25 09:27:03 +00:00
const Vehicle * u = v ;
2004-08-09 17:04:08 +00:00
do {
2007-01-24 07:14:09 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( 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 ) ;
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* no smoke? */
2007-01-30 11:53:35 +00:00
if ( ( rvi - > railveh_type = = RAILVEH_WAGON & & effect_type = = 0 ) | |
2005-11-04 12:58:18 +00:00
disable_effect | |
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
2007-04-04 03:21:14 +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 ;
2007-04-04 03:21:14 +00:00
/* No sparks for electric vehicles on nonelectrified tracks */
2007-02-25 11:36:19 +00:00
if ( ! HasPowerOnRail ( v - > u . rail . railtype , GetTileRailType ( v - > tile ) ) ) continue ;
2005-11-07 13:02:33 +00:00
2005-11-04 12:58:18 +00:00
if ( effect_type = = 0 ) {
2007-04-04 03:21:14 +00:00
/* Use default effect type for engine class. */
2007-01-24 07:14:09 +00:00
effect_type = rvi - > engclass ;
2005-11-04 12:58:18 +00:00
} else {
effect_type - - ;
}
2007-02-25 09:27:03 +00:00
int x = _vehicle_smoke_pos [ v - > direction ] * effect_offset ;
int y = _vehicle_smoke_pos [ ( v - > direction + 2 ) % 8 ] * effect_offset ;
2005-11-04 12:58:18 +00:00
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 :
2007-04-04 03:21:14 +00:00
/* 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 :
2007-04-04 03:21:14 +00:00
/* 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 :
2007-04-04 03:21:14 +00:00
/* 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 ;
}
2007-08-30 13:03:56 +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 ,
2007-05-06 19:51:25 +00:00
SND_0A_TRAIN_HORN ,
SND_47_MAGLEV_2 ,
SND_41_MAGLEV
2004-12-04 09:26:39 +00:00
} ;
2006-09-27 18:17:01 +00:00
if ( PlayVehicleSound ( v , VSE_START ) ) return ;
2007-02-25 09:27:03 +00:00
EngineID engtype = v - > engine_type ;
2007-05-06 19:51:25 +00:00
SndPlayVehicleFx ( sfx [ RailVehInfo ( engtype ) - > engclass ] , v ) ;
2004-08-09 17:04:08 +00:00
}
2007-05-07 15:58:05 +00:00
void Train : : PlayLeaveStationSound ( ) const
{
TrainPlayLeaveStationSound ( this ) ;
}
2004-08-09 17:04:08 +00:00
static bool CheckTrainStayInDepot ( Vehicle * v )
{
2007-04-04 03:21:14 +00:00
/* bail out if not all wagons are in the same depot or not in a depot at all */
2007-08-30 13:03:56 +00:00
for ( const Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2007-02-13 10:46:45 +00:00
if ( u - > u . rail . track ! = TRACK_BIT_DEPOT | | u - > tile ! = v - > tile ) return false ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* if the train got no power, then keep it in the depot */
2006-08-12 11:08:02 +00:00
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
2007-01-10 18:56:51 +00:00
v - > u . rail . track = TRACK_BIT_X ;
if ( v - > direction & 2 ) v - > u . rail . track = TRACK_BIT_Y ;
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
2007-05-01 16:35:14 +00:00
v - > UpdateDeltaXY ( v - > direction ) ;
2007-07-01 19:11:47 +00:00
v - > cur_image = v - > GetImage ( v - > direction ) ;
2004-08-09 17:04:08 +00:00
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 ) ;
2006-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
2004-08-09 17:04:08 +00:00
return false ;
}
2005-01-31 11:23:10 +00:00
/* Check for station tiles */
2007-03-07 12:11:48 +00:00
struct TrainTrackFollowerData {
2004-08-09 17:04:08 +00:00
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 ;
2007-01-10 18:56:51 +00:00
TrackdirByte best_track ;
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2007-01-10 18:56:51 +00:00
static bool NtpCallbFindStation ( TileIndex tile , TrainTrackFollowerData * ttfd , Trackdir track , uint length )
2005-06-24 12:38:35 +00:00
{
2007-04-04 03:21:14 +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
2007-04-04 03:21:14 +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 */
2007-04-04 03:21:14 +00:00
/* found station */
2005-07-19 11:42:40 +00:00
ttfd - > best_track = track ;
2007-03-13 21:04:22 +00:00
ttfd - > best_bird_dist = 0 ;
2004-08-09 17:04:08 +00:00
return true ;
} else {
2007-04-04 03:21:14 +00:00
/* didn't find station, keep track of the best path so far. */
2007-02-25 09:27:03 +00:00
uint 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 ] = {
2007-04-04 03:21:14 +00:00
{ 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
2004-08-09 17:04:08 +00:00
} ;
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 */
2007-01-10 18:56:51 +00:00
static Track ChooseTrainTrack ( Vehicle * v , TileIndex tile , DiagDirection enterdir , TrackBits tracks )
2004-09-10 19:02:27 +00:00
{
2007-01-10 18:56:51 +00:00
Track best_track ;
2007-04-04 03:21:14 +00:00
/* pathfinders are able to tell that route was only 'guessed' */
2006-10-17 16:16:19 +00:00
bool path_not_found = false ;
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
2007-01-10 18:56:51 +00:00
assert ( ( tracks & ~ 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 */
2007-01-10 18:56:51 +00:00
if ( KILL_FIRST_BIT ( tracks ) = = 0 ) return FindFirstTrack ( tracks ) ;
2004-08-09 17:04:08 +00:00
2006-05-27 16:12:16 +00:00
if ( _patches . yapf . rail_use_yapf ) {
2007-01-10 18:56:51 +00:00
Trackdir trackdir = YapfChooseRailTrack ( v , tile , enterdir , tracks , & path_not_found ) ;
2006-05-27 16:12:16 +00:00
if ( trackdir ! = INVALID_TRACKDIR ) {
best_track = TrackdirToTrack ( trackdir ) ;
} else {
2007-01-10 18:56:51 +00:00
best_track = FindFirstTrack ( tracks ) ;
2006-05-27 16:12:16 +00:00
}
} else if ( _patches . new_pathfinding_all ) { /* Use a new pathfinding for everything */
void * perf = NpfBeginInterval ( ) ;
2005-01-31 11:23:10 +00:00
NPFFindStationOrTileData fstd ;
NPFFillWithOrderData ( & fstd , v ) ;
/* The enterdir for the new tile, is the exitdir for the old tile */
2007-02-25 09:27:03 +00:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
2005-01-31 11:23:10 +00:00
assert ( trackdir ! = 0xff ) ;
2007-05-24 22:41:50 +00:00
NPFFoundTargetData ftd = NPFRouteToStationOrTile ( tile - TileOffsByDiagDir ( enterdir ) , trackdir , & fstd , TRANSPORT_RAIL , 0 , v - > owner , v - > u . rail . compatible_railtypes ) ;
2005-04-15 13:48:08 +00:00
if ( ftd . best_trackdir = = 0xff ) {
2007-04-04 03:21:14 +00:00
/* We are already at our target. Just do something
* @ todo maybe display error ?
* @ todo : go straight ahead if possible ? */
2007-01-10 18:56:51 +00:00
best_track = FindFirstTrack ( tracks ) ;
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 . */
2006-10-17 16:16:19 +00:00
if ( ftd . best_bird_dist ! = 0 ) path_not_found = true ;
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
2007-02-25 09:27:03 +00:00
int time = NpfEndInterval ( perf ) ;
2006-12-26 17:36:18 +00:00
DEBUG ( yapf , 4 , " [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 ( ) ;
2007-02-25 09:27:03 +00:00
TrainTrackFollowerData fd ;
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 ;
2007-01-10 18:56:51 +00:00
fd . best_track = INVALID_TRACKDIR ;
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
2007-04-04 03:21:14 +00:00
/* check whether the path was found or only 'guessed' */
2006-10-17 16:16:19 +00:00
if ( fd . best_bird_dist ! = 0 ) path_not_found = true ;
2005-07-12 20:28:19 +00:00
if ( fd . best_track = = 0xff ) {
2007-04-04 03:21:14 +00:00
/* blaha */
2007-01-10 18:56:51 +00:00
best_track = FindFirstTrack ( tracks ) ;
2005-01-31 11:23:10 +00:00
} else {
2007-01-10 18:56:51 +00:00
best_track = TrackdirToTrack ( fd . best_track ) ;
2005-01-31 11:23:10 +00:00
}
2006-05-27 16:12:16 +00:00
2007-02-25 09:27:03 +00:00
int time = NpfEndInterval ( perf ) ;
2006-12-26 17:36:18 +00:00
DEBUG ( yapf , 4 , " [NTPT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , 0 , 0 ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 03:21:14 +00:00
/* handle "path not found" state */
2006-10-17 16:16:19 +00:00
if ( path_not_found ) {
2007-04-04 03:21:14 +00:00
/* PF didn't find the route */
2006-10-17 16:16:19 +00:00
if ( ! HASBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
2007-04-04 03:21:14 +00:00
/* it is first time the problem occurred, set the "path not found" flag */
2006-10-17 16:16:19 +00:00
SETBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
2007-04-04 03:21:14 +00:00
/* and notify user about the event */
2006-10-29 13:50:39 +00:00
if ( _patches . lost_train_warn & & v - > owner = = _local_player ) {
2006-10-17 16:16:19 +00:00
SetDParam ( 0 , v - > unitnumber ) ;
AddNewsItem (
STR_TRAIN_IS_LOST ,
NEWS_FLAGS ( NM_SMALL , NF_VIEWPORT | NF_VEHICLE , NT_ADVICE , 0 ) ,
v - > index ,
0 ) ;
}
}
} else {
2007-04-04 03:21:14 +00:00
/* route found, is the train marked with "path not found" flag? */
2006-10-17 16:16:19 +00:00
if ( HASBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
2007-04-04 03:21:14 +00:00
/* clear the flag as the PF's problem was solved */
2006-10-17 16:16:19 +00:00
CLRBIT ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
2007-04-04 03:21:14 +00:00
/* can we also delete the "News" item somehow? */
2006-10-17 16:16:19 +00:00
}
}
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 )
{
if ( _opt . diff . line_reverse_mode ! = 0 | |
2007-02-13 10:46:45 +00:00
v - > u . rail . track = = TRACK_BIT_DEPOT | | v - > u . rail . track = = TRACK_BIT_WORMHOLE | |
2004-08-09 17:04:08 +00:00
! ( v - > direction & 1 ) )
return false ;
2007-02-25 09:27:03 +00:00
TrainTrackFollowerData fd ;
2004-08-09 17:04:08 +00:00
FillWithStationData ( & fd , v ) ;
2007-02-25 09:27:03 +00:00
uint reverse_best = 0 ;
2004-08-09 17:04:08 +00:00
assert ( v - > u . rail . track ) ;
2007-02-25 09:27:03 +00:00
int 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 ;
2007-01-10 18:56:51 +00:00
Trackdir trackdir , trackdir_rev ;
2005-01-31 11:23:10 +00:00
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 ) ;
2007-05-24 22:41:50 +00:00
ftd = NPFRouteToStationOrTileTwoWay ( v - > tile , trackdir , last - > tile , trackdir_rev , & fstd , TRANSPORT_RAIL , 0 , 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 {
2007-02-25 09:27:03 +00:00
int best_track = - 1 ;
uint reverse = 0 ;
uint best_bird_dist = 0 ;
uint best_track_dist = 0 ;
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 ;
2007-01-10 18:56:51 +00:00
NewTrainPathfind ( v - > tile , v - > dest_tile , v - > u . rail . compatible_railtypes , ( DiagDirection ) ( 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 . */
2007-02-25 09:27:03 +00:00
int 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 )
{
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 ) ) {
2007-06-20 19:17:22 +00:00
UpdateVehicleTimetable ( v , true ) ;
2007-06-25 23:14:13 +00:00
v - > cur_order_index + + ;
2006-02-10 06:33:56 +00:00
}
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
}
2007-06-28 22:03:34 +00:00
/**
* Reversing because of order change is allowed only just after leaving a
* station ( and the difficulty setting to allowed , of course )
* this can be detected because only after OT_LEAVESTATION , current_order
* will be reset to nothing . ( That also happens if no order , but in that case
* it won ' t hit the point in code where may_reverse is checked )
*/
bool may_reverse = v - > current_order . type = = OT_NOTHING ;
2007-04-04 03:21:14 +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 ) {
2007-06-25 23:14:13 +00:00
UpdateVehicleTimetable ( v , true ) ;
2004-08-09 17:04:08 +00:00
v - > cur_order_index + + ;
}
2007-04-04 03:21:14 +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 ) ) {
2007-06-20 19:17:22 +00:00
UpdateVehicleTimetable ( v , true ) ;
2007-06-25 23:14:13 +00:00
v - > cur_order_index + + ;
2004-08-09 17:04:08 +00:00
}
2007-04-04 03:21:14 +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
2007-02-25 09:27:03 +00:00
const Order * order = GetVehicleOrder ( v , v - > cur_order_index ) ;
2004-08-09 17:04:08 +00:00
2007-04-04 03:21:14 +00:00
/* If no order, do nothing. */
2005-01-15 19:06:22 +00:00
if ( order = = NULL ) {
2007-03-08 21:39:34 +00:00
v - > current_order . Free ( ) ;
2004-08-09 17:04:08 +00:00
v - > dest_tile = 0 ;
return false ;
}
2007-04-04 03:21:14 +00:00
/* 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 ;
2007-04-04 03:21:14 +00:00
/* 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
2007-06-28 22:03:34 +00:00
return may_reverse & & CheckReverseTrain ( v ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-29 22:33:51 +00:00
void Train : : MarkDirty ( )
2004-08-09 17:04:08 +00:00
{
2007-04-29 22:33:51 +00:00
Vehicle * v = this ;
2004-08-09 17:04:08 +00:00
do {
2007-07-01 19:11:47 +00:00
v - > cur_image = v - > GetImage ( v - > direction ) ;
2004-08-09 17:04:08 +00:00
MarkAllViewportsDirty ( v - > left_coord , v - > top_coord , v - > right_coord + 1 , v - > bottom_coord + 1 ) ;
2007-08-30 13:03:56 +00:00
} while ( ( v = v - > Next ( ) ) ! = NULL ) ;
2007-04-29 22:33:51 +00:00
/* need to update acceleration and cached values since the goods on the train changed. */
TrainCargoChanged ( this ) ;
UpdateTrainAcceleration ( this ) ;
2004-08-09 17:04:08 +00:00
}
static int UpdateTrainSpeed ( Vehicle * v )
{
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
}
2007-02-25 09:27:03 +00:00
uint spd = v - > subspeed + accel * 2 ;
2004-08-09 17:04:08 +00:00
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
{
v - > last_station_visited = station ;
/* check if a train ever visited this station before */
2007-02-25 09:27:03 +00:00
Station * 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 ) ;
2007-02-25 09:27:03 +00:00
uint32 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 ) ;
2004-08-09 17:04:08 +00:00
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
}
2007-04-29 18:21:24 +00:00
v - > BeginLoading ( ) ;
2006-09-03 08:25:27 +00:00
v - > current_order . dest = 0 ;
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
{
2007-02-25 09:27:03 +00:00
byte old_z = v - > z_pos ;
2007-06-26 20:03:17 +00:00
v - > z_pos = GetSlopeZ ( v - > x_pos , v - > y_pos ) ;
2004-08-09 17:04:08 +00:00
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
2007-06-26 20:03:17 +00:00
if ( v - > u . rail . track = = TRACK_BIT_X | | v - > u . rail . track = = TRACK_BIT_Y ) {
/* Any track that isn't TRACK_BIT_X or TRACK_BIT_Y cannot be sloped.
2007-06-25 08:24:03 +00:00
* To check whether the current tile is sloped , and in which
* direction it is sloped , we get the ' z ' at the center of
* the tile ( middle_z ) and the edge of the tile ( old_z ) ,
* which we then can compare . */
static const int HALF_TILE_SIZE = TILE_SIZE / 2 ;
static const int INV_TILE_SIZE_MASK = ~ ( TILE_SIZE - 1 ) ;
byte middle_z = GetSlopeZ ( ( v - > x_pos & INV_TILE_SIZE_MASK ) | HALF_TILE_SIZE , ( v - > y_pos & INV_TILE_SIZE_MASK ) | HALF_TILE_SIZE ) ;
/* For some reason tunnel tiles are always given as sloped :(
* But they are not sloped . . . */
2007-06-26 20:03:17 +00:00
if ( middle_z ! = v - > z_pos & & ! IsTunnelTile ( TileVirtXY ( v - > x_pos , v - > y_pos ) ) ) {
2007-06-25 08:24:03 +00:00
SETBIT ( v - > u . rail . flags , ( middle_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 ] = {
2007-01-10 18:56:51 +00:00
DIR_N , DIR_NW , DIR_W , INVALID_DIR ,
DIR_NE , DIR_N , DIR_SW , INVALID_DIR ,
2006-03-08 06:55:33 +00:00
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-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 ) | |
2007-02-27 23:36:28 +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
}
2007-03-07 12:11:48 +00:00
struct RailtypeSlowdownParams {
2004-08-09 17:04:08 +00:00
byte small_turn , large_turn ;
byte z_up ; // fraction to remove when moving up
byte z_down ; // fraction to remove when moving down
2007-03-07 12:11:48 +00:00
} ;
2004-08-09 17:04:08 +00:00
2006-03-29 16:30:26 +00:00
static const RailtypeSlowdownParams _railtype_slowdown [ ] = {
2004-08-09 17:04:08 +00:00
// normal accel
2007-04-04 03:21:14 +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
} ;
2007-04-04 03:21:14 +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
if ( _patches . realistic_acceleration ) return ;
2007-02-25 09:27:03 +00:00
DirDiff diff = DirDifference ( v - > direction , new_dir ) ;
2006-03-08 07:48:56 +00:00
if ( diff = = DIRDIFF_SAME ) return ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
const RailtypeSlowdownParams * 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
}
2007-04-04 03:21:14 +00:00
/** Modify the speed of the vehicle due to a change in altitude */
2004-08-09 17:04:08 +00:00
static void AffectSpeedByZChange ( Vehicle * v , byte old_z )
{
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
2007-02-25 09:27:03 +00:00
const RailtypeSlowdownParams * rsp = & _railtype_slowdown [ v - > u . rail . railtype ] ;
2004-08-09 17:04:08 +00:00
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 [ ] = {
2007-01-10 18:56:51 +00:00
DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_NE , DIAGDIR_SE , DIAGDIR_SW , DIAGDIR_SE , INVALID_DIAGDIR , INVALID_DIAGDIR ,
2006-03-09 12:32:25 +00:00
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 ] ) ;
}
}
static void SetVehicleCrashed ( Vehicle * v )
{
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
2007-02-25 09:27:03 +00:00
Vehicle * u = v ;
2004-08-09 17:04:08 +00:00
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 )
2007-06-22 11:58:59 +00:00
if ( IsCargoInClass ( v - > cargo_type , CC_PASSENGERS ) ) num + = v - > cargo . Count ( ) ;
2004-08-09 17:04:08 +00:00
END_ENUM_WAGONS ( v )
return num ;
}
2007-06-19 16:40:59 +00:00
struct TrainCollideChecker {
Vehicle * v ;
const Vehicle * v_skip ;
uint num ;
} ;
static void * FindTrainCollideEnum ( Vehicle * v , void * data )
{
TrainCollideChecker * tcc = ( TrainCollideChecker * ) data ;
if ( v ! = tcc - > v & &
v ! = tcc - > v_skip & &
v - > type = = VEH_TRAIN & &
v - > u . rail . track ! = TRACK_BIT_DEPOT & &
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 ) {
2007-08-30 21:11:12 +00:00
Vehicle * coll = v - > First ( ) ;
2007-06-19 16:40:59 +00:00
/* it can't collide with its own wagons */
if ( tcc - > v = = coll | |
( tcc - > v - > u . rail . track = = TRACK_BIT_WORMHOLE & & ( tcc - > v - > direction & 2 ) ! = ( v - > direction & 2 ) ) )
return NULL ;
/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
if ( ! ( tcc - > v - > vehstatus & VS_CRASHED ) ) {
tcc - > num + = 2 + CountPassengersInTrain ( tcc - > v ) ;
SetVehicleCrashed ( tcc - > v ) ;
}
if ( ! ( coll - > vehstatus & VS_CRASHED ) ) {
/* two drivers + passengers killed in train coll (if it was not crashed already) */
tcc - > num + = 2 + CountPassengersInTrain ( coll ) ;
SetVehicleCrashed ( coll ) ;
}
}
return NULL ;
}
2007-04-04 03:21:14 +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 )
{
/* can't collide in depot */
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track = = TRACK_BIT_DEPOT ) return ;
2004-09-10 19:02:27 +00:00
2007-02-13 10:46:45 +00:00
assert ( v - > u . rail . track = = TRACK_BIT_WORMHOLE | | TileVirtXY ( v - > x_pos , v - > y_pos ) = = v - > tile ) ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
TrainCollideChecker tcc ;
2004-08-09 17:04:08 +00:00
tcc . v = v ;
2007-08-30 13:03:56 +00:00
tcc . v_skip = v - > Next ( ) ;
2007-06-19 16:40:59 +00:00
tcc . num = 0 ;
2004-08-09 17:04:08 +00:00
2007-06-19 16:40:59 +00:00
/* find colliding vehicles */
2007-07-30 08:49:41 +00:00
if ( v - > u . rail . track = = TRACK_BIT_WORMHOLE ) {
VehicleFromPos ( v - > tile , & tcc , FindTrainCollideEnum ) ;
if ( IsBridgeTile ( v - > tile ) ) {
VehicleFromPos ( GetOtherBridgeEnd ( v - > tile ) , & tcc , FindTrainCollideEnum ) ;
} else {
VehicleFromPos ( GetOtherTunnelEnd ( v - > tile ) , & tcc , FindTrainCollideEnum ) ;
}
} else {
VehicleFromPosXY ( v - > x_pos , v - > y_pos , & tcc , FindTrainCollideEnum ) ;
}
2004-08-09 17:04:08 +00:00
2007-06-19 16:40:59 +00:00
/* any dead -> no crash */
if ( tcc . num = = 0 ) return ;
2004-09-10 19:02:27 +00:00
2007-06-19 16:40:59 +00:00
SetDParam ( 0 , tcc . 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
}
2007-03-07 12:11:48 +00:00
struct VehicleAtSignalData {
2005-03-06 12:41:18 +00:00
TileIndex tile ;
2006-03-08 06:55:33 +00:00
Direction direction ;
2007-03-07 12:11:48 +00:00
} ;
2005-03-06 12:41:18 +00:00
2004-08-09 17:04:08 +00:00
static void * CheckVehicleAtSignal ( Vehicle * v , void * data )
{
2007-01-10 18:56:51 +00:00
const VehicleAtSignalData * vasd = ( VehicleAtSignalData * ) data ;
2005-03-06 12:41:18 +00:00
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN & & IsFrontEngine ( v ) & & v - > tile = = vasd - > tile ) {
2006-03-08 07:48:56 +00:00
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
}
2006-12-05 20:22:14 +00:00
static void TrainController ( Vehicle * v , bool update_image )
2004-08-09 17:04:08 +00:00
{
2005-06-21 14:50:08 +00:00
Vehicle * prev ;
2004-08-09 17:04:08 +00:00
2004-08-11 22:07:08 +00:00
/* For every vehicle after and including the given vehicle */
2007-08-30 21:11:12 +00:00
for ( prev = v - > Previous ( ) ; v ! = NULL ; prev = v , v = v - > Next ( ) ) {
2007-06-12 11:22:32 +00:00
DiagDirection enterdir = DIAGDIR_BEGIN ;
bool update_signals = false ;
2004-08-09 17:04:08 +00:00
BeginVehicleMove ( v ) ;
2004-09-10 19:02:27 +00:00
2007-02-25 10:49:13 +00:00
GetNewVehiclePosResult gp = GetNewVehiclePos ( v ) ;
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track ! = TRACK_BIT_WORMHOLE ) {
2004-08-11 22:07:08 +00:00
/* Not inside tunnel */
2007-02-25 10:25:25 +00:00
if ( gp . old_tile = = gp . new_tile ) {
2004-08-11 22:07:08 +00:00
/* Staying in the old tile */
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track = = TRACK_BIT_DEPOT ) {
2007-02-13 11:29:20 +00:00
/* Inside depot */
2004-08-09 17:04:08 +00:00
gp . x = v - > x_pos ;
gp . y = v - > y_pos ;
} else {
2007-02-13 11:29:20 +00:00
/* Not inside depot */
2004-12-21 16:00:14 +00:00
2007-01-14 19:57:49 +00:00
if ( IsFrontEngine ( v ) & & ! TrainCheckIfLineEnds ( v ) ) return ;
2004-12-21 16:00:14 +00:00
2007-02-25 09:27:03 +00:00
uint32 r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
2007-02-13 10:43:50 +00:00
if ( HASBIT ( r , VETS_CANNOT_ENTER ) ) {
2004-08-09 17:04:08 +00:00
goto invalid_rail ;
2005-01-31 11:23:10 +00:00
}
2007-02-13 10:26:53 +00:00
if ( HASBIT ( r , VETS_ENTERED_STATION ) ) {
TrainEnterStation ( v , r > > VETS_STATION_ID_OFFSET ) ;
2004-08-09 17:04:08 +00:00
return ;
}
2004-12-05 12:43:04 +00:00
if ( v - > current_order . type = = OT_LEAVESTATION ) {
2007-03-08 21:39:34 +00:00
v - > current_order . Free ( ) ;
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. */
/* Determine what direction we're entering the new tile from */
2007-02-25 09:47:46 +00:00
Direction dir = GetNewVehicleDirectionByTile ( gp . new_tile , gp . old_tile ) ;
2007-06-12 11:22:32 +00:00
enterdir = DirToDiagDir ( dir ) ;
2007-02-13 11:29:20 +00:00
assert ( IsValidDiagDirection ( enterdir ) ) ;
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 . */
2007-05-24 22:41:50 +00:00
uint32 ts = GetTileTrackStatus ( gp . new_tile , TRANSPORT_RAIL , 0 ) & _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 . */
2007-02-25 09:27:03 +00:00
uint32 tracks = ts | ( ts > > 8 ) ;
2007-02-25 09:47:46 +00:00
TrackBits bits = ( TrackBits ) ( tracks & TRACK_BIT_MASK ) ;
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 */
2007-01-10 18:56:51 +00:00
bits & = ~ TrackCrossesTracks ( FindFirstTrack ( v - > u . rail . track ) ) ;
2006-02-13 21:15:00 +00:00
}
2005-01-31 11:23:10 +00:00
2007-02-13 11:29:20 +00:00
if ( bits = = TRACK_BIT_NONE ) goto invalid_rail ;
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 . */
2007-02-13 11:29:20 +00:00
if ( ! CheckCompatibleRail ( v , gp . new_tile ) ) goto invalid_rail ;
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
TrackBits chosen_track ;
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 */
2007-02-25 11:49:43 +00:00
chosen_track = TrackToTrackBits ( ChooseTrainTrack ( v , gp . new_tile , enterdir , bits ) ) ;
2005-01-31 11:23:10 +00:00
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. */
2007-02-25 09:47:46 +00:00
if ( ( tracks > > 16 ) & chosen_track & & v - > u . rail . force_proceed = = 0 ) {
2007-04-04 03:21:14 +00:00
/* In front of a red signal
* find the first set bit in ts . need to do it in 2 steps , since
2007-02-25 09:47:46 +00:00
* FIND_FIRST_BIT only handles 6 bits at a time . */
Trackdir i = FindFirstTrackdir ( ( TrackdirBits ) ( uint16 ) ts ) ;
if ( ! HasSignalOnTrackdir ( gp . new_tile , ReverseTrackdir ( i ) ) ) {
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 100 ;
if ( + + v - > load_unload_time_rem < _patches . wait_oneway_signal * 20 ) return ;
} else if ( HasSignalOnTrackdir ( gp . new_tile , i ) ) {
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
2007-04-18 22:10:36 +00:00
v - > progress = 255 - 10 ;
2007-02-25 09:47:46 +00:00
if ( + + v - > load_unload_time_rem < _patches . wait_twoway_signal * 73 ) {
TileIndex o_tile = gp . new_tile + TileOffsByDiagDir ( enterdir ) ;
VehicleAtSignalData vasd ;
vasd . tile = o_tile ;
vasd . direction = ReverseDir ( dir ) ;
/* check if a train is waiting on the other side */
if ( VehicleFromPos ( o_tile , & vasd , CheckVehicleAtSignal ) = = NULL ) return ;
}
}
goto reverse_train_direction ;
}
2004-08-09 17:04:08 +00:00
} else {
2007-02-13 11:29:20 +00:00
static const TrackBits _matching_tracks [ 8 ] = {
TRACK_BIT_LEFT | TRACK_BIT_RIGHT , TRACK_BIT_X ,
TRACK_BIT_UPPER | TRACK_BIT_LOWER , TRACK_BIT_Y ,
TRACK_BIT_LEFT | TRACK_BIT_RIGHT , TRACK_BIT_X ,
TRACK_BIT_UPPER | TRACK_BIT_LOWER , TRACK_BIT_Y
} ;
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. */
2007-01-10 18:56:51 +00:00
chosen_track = ( TrackBits ) ( byte ) ( _matching_tracks [ GetDirectionToVehicle ( prev , gp . x , gp . y ) ] & bits ) ;
2004-08-09 17:04:08 +00:00
}
2007-02-13 11:29:20 +00:00
/* Make sure chosen track is a valid track */
assert (
chosen_track = = TRACK_BIT_X | | chosen_track = = TRACK_BIT_Y | |
chosen_track = = TRACK_BIT_UPPER | | chosen_track = = TRACK_BIT_LOWER | |
chosen_track = = TRACK_BIT_LEFT | | chosen_track = = TRACK_BIT_RIGHT ) ;
2004-08-09 17:04:08 +00:00
/* Update XY to reflect the entrance to the new tile, and select the direction to use */
2007-02-25 09:27:03 +00:00
const byte * b = _initial_tile_subcoord [ FIND_FIRST_BIT ( chosen_track ) ] [ enterdir ] ;
gp . x = ( gp . x & ~ 0xF ) | b [ 0 ] ;
gp . y = ( gp . y & ~ 0xF ) | b [ 1 ] ;
Direction chosen_dir = ( Direction ) 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 */
2007-02-25 09:27:03 +00:00
uint32 r = VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) ;
2007-02-13 10:26:53 +00:00
if ( HASBIT ( r , VETS_CANNOT_ENTER ) ) {
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
2007-08-30 13:03:56 +00:00
if ( IsLevelCrossingTile ( v - > tile ) & & v - > Next ( ) = = NULL ) {
2006-04-25 07:32:33 +00:00
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
2007-02-13 10:26:53 +00:00
if ( ! HASBIT ( r , VETS_ENTERED_WORMHOLE ) ) {
2004-08-09 17:04:08 +00:00
v - > tile = gp . new_tile ;
2006-03-29 16:30:26 +00:00
2007-02-25 11:36:19 +00:00
if ( GetTileRailType ( gp . new_tile ) ! = GetTileRailType ( gp . old_tile ) ) {
2007-08-30 21:11:12 +00:00
TrainPowerChanged ( v - > First ( ) ) ;
2006-03-29 16:30:26 +00:00
}
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
}
2007-06-12 11:22:32 +00:00
/* We need to update signal status, but after the vehicle position hash
* has been updated by AfterSetTrainPos ( ) */
update_signals = true ;
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 {
2007-02-13 11:29:20 +00:00
/* In tunnel or on a bridge */
2007-02-24 18:44:30 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
v - > cur_speed =
min ( v - > cur_speed , GetBridge ( GetBridgeType ( v - > tile ) ) - > speed ) ;
}
2006-12-27 12:38:02 +00:00
2007-02-13 10:26:53 +00:00
if ( ! ( IsTunnelTile ( gp . new_tile ) | | IsBridgeTile ( gp . new_tile ) ) | | ! HASBIT ( VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) , VETS_ENTERED_WORMHOLE ) ) {
2005-07-19 11:42:40 +00:00
v - > x_pos = gp . x ;
v - > y_pos = gp . y ;
VehiclePositionChanged ( v ) ;
2006-12-27 12:38:02 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) EndVehicleMove ( v ) ;
2005-07-19 11:42:40 +00:00
continue ;
2004-08-09 17:04:08 +00:00
}
}
/* update image of train, as well as delta XY */
2007-02-25 09:27:03 +00:00
Direction newdir = GetNewVehicleDirection ( v , gp . x , gp . y ) ;
2007-05-01 16:35:14 +00:00
v - > UpdateDeltaXY ( newdir ) ;
2007-07-01 19:11:47 +00:00
if ( update_image ) v - > cur_image = v - > GetImage ( 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 */
2007-02-25 09:27:03 +00:00
byte 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 ) ;
}
2007-06-12 11:22:32 +00:00
if ( update_signals ) {
if ( IsFrontEngine ( v ) ) TrainMovedChangeSignals ( gp . new_tile , enterdir ) ;
/* Signals can only change when the first
* ( above ) or the last vehicle moves . */
2007-08-30 13:03:56 +00:00
if ( v - > Next ( ) = = NULL ) TrainMovedChangeSignals ( gp . old_tile , ReverseDiagDir ( enterdir ) ) ;
2007-06-12 11:22:32 +00:00
}
2004-08-09 17:04:08 +00:00
}
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-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
2007-04-18 00:41:09 +00:00
* @ param v the Vehicle of which last wagon is to be removed
2005-03-03 23:26:35 +00:00
*/
2004-08-09 17:04:08 +00:00
static void DeleteLastWagon ( Vehicle * 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 */
2007-02-25 09:27:03 +00:00
Vehicle * u = v ;
2007-08-30 13:03:56 +00:00
for ( ; v - > Next ( ) ! = NULL ; v = v - > Next ( ) ) u = v ;
u - > SetNext ( NULL ) ;
2004-08-09 17:04:08 +00:00
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 ) ;
2007-05-19 09:40:18 +00:00
2007-08-03 19:36:00 +00:00
delete v ;
2004-08-09 17:04:08 +00:00
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track ! = TRACK_BIT_DEPOT & & v - > u . rail . track ! = TRACK_BIT_WORMHOLE )
2005-03-03 23:26:35 +00:00
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 ) ;
2007-02-13 10:46:45 +00:00
if ( ( v - > u . rail . track = = TRACK_BIT_WORMHOLE & & v - > vehstatus & VS_HIDDEN ) ) { // 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-12-27 12:38:02 +00:00
/* We don't need to twist around vehicles if they're not visible */
2006-06-07 19:35:21 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
2006-12-27 12:38:02 +00:00
v - > direction = ChangeDir ( v - > direction , delta [ GB ( Random ( ) , 0 , 2 ) ] ) ;
2004-08-09 17:04:08 +00:00
BeginVehicleMove ( v ) ;
2007-05-01 16:35:14 +00:00
v - > UpdateDeltaXY ( v - > direction ) ;
2007-07-01 19:11:47 +00:00
v - > cur_image = v - > GetImage ( v - > direction ) ;
2006-12-27 12:38:02 +00:00
/* Refrain from updating the z position of the vehicle when on
a bridge , because AfterSetTrainPos will put the vehicle under
the bridge in that case */
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track ! = TRACK_BIT_WORMHOLE ) AfterSetTrainPos ( v , false ) ;
2004-08-09 17:04:08 +00:00
}
2007-08-30 13:03:56 +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-09-10 19:02:27 +00:00
2007-02-13 10:46:45 +00:00
if ( state = = 4 & & ! ( v - > vehstatus & VS_HIDDEN ) ) {
2005-02-12 15:53:32 +00:00
CreateEffectVehicleRel ( v , 4 , 4 , 8 , EV_EXPLOSION_LARGE ) ;
2004-08-09 17:04:08 +00:00
}
2007-02-25 09:27:03 +00:00
uint32 r ;
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
2007-02-25 09:27:03 +00:00
Vehicle * u = v ;
2004-08-09 17:04:08 +00:00
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 ;
}
2007-08-30 13:03:56 +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 ) ;
2007-05-19 09:40:18 +00:00
InvalidateWindow ( WC_REPLACE_VEHICLE , ( v - > group_id < < 16 ) | VEH_TRAIN ) ;
2005-01-23 22:01:51 +00:00
}
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 ) ) {
2007-03-22 03:42:43 +00:00
SndPlayVehicleFx ( ( _opt . landscape ! = LT_TOYLAND ) ?
2006-09-27 18:17:01 +00:00
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 ) ;
2007-07-26 15:37:19 +00:00
if ( u ! = NULL ) u - > u . special . animation_state = 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
{
2007-02-25 09:27:03 +00:00
int t = v - > breakdown_ctr ;
2005-10-03 21:20:01 +00:00
if ( t > 1 ) {
2004-08-09 17:04:08 +00:00
v - > vehstatus | = VS_TRAIN_SLOWING ;
2004-09-10 19:02:27 +00:00
2007-02-25 09:27:03 +00:00
uint16 break_speed = _breakdown_speeds [ GB ( ~ t , 4 , 4 ) ] ;
2005-10-03 21:20:01 +00:00
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 ;
}
2007-02-13 10:46:45 +00:00
if ( v - > u . rail . track = = TRACK_BIT_WORMHOLE ) return true ; // exit if inside a tunnel
if ( v - > u . rail . track = = TRACK_BIT_DEPOT ) return true ; // exit if inside a depot
2005-07-04 14:58:55 +00:00
2007-02-25 09:27:03 +00:00
TileIndex tile = v - > tile ;
2004-08-09 17:04:08 +00:00
2006-12-27 12:38:02 +00:00
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) ) {
2007-02-25 09:27:03 +00:00
DiagDirection dir = IsTunnel ( tile ) ? GetTunnelDirection ( tile ) : GetBridgeRampDirection ( tile ) ;
2006-12-27 12:38:02 +00:00
if ( DiagDirToDir ( dir ) = = 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 */
2007-02-25 09:27:03 +00:00
DiagDirection dir = DirToDiagDir ( v - > direction ) ;
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-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.
2007-05-24 22:41:50 +00:00
uint32 ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL , 0 ) & _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 ?? */
2007-02-25 09:27:03 +00:00
uint x = v - > x_pos & 0xF ;
uint 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 ;
2007-01-10 18:56:51 +00:00
default : 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 ) {
2007-04-04 03:21:14 +00:00
/* 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
}
2007-04-04 03:21:14 +00:00
/* slow down */
2004-08-09 17:04:08 +00:00
v - > vehstatus | = VS_TRAIN_SLOWING ;
2007-02-25 09:27:03 +00:00
uint16 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 )
{
/* train has crashed? */
2007-08-26 20:16:02 +00:00
if ( v - > vehstatus & VS_CRASHED ) {
2004-08-09 17:04:08 +00:00
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 ;
}
2007-05-07 16:21:34 +00:00
v - > HandleLoading ( mode ) ;
2004-08-09 17:04:08 +00:00
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 ) ;
2007-02-25 09:27:03 +00:00
int j = UpdateTrainSpeed ( v ) ;
2004-08-09 17:04:08 +00:00
if ( j = = 0 ) {
2007-04-04 03:21:14 +00:00
/* 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 {
2006-12-05 20:22:14 +00:00
TrainController ( v , true ) ;
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 ) ;
}
2007-08-29 21:27:16 +00:00
Money Train : : GetRunningCost ( ) const
{
Money cost = 0 ;
const Vehicle * v = this ;
do {
const RailVehicleInfo * rvi = RailVehInfo ( v - > engine_type ) ;
byte cost_factor = GetVehicleProperty ( v , 0x0D , rvi - > running_cost_base ) ;
if ( cost_factor = = 0 ) continue ;
cost + = cost_factor * _price . running_rail [ rvi - > running_cost_class ] ;
} while ( ( v = GetNextVehicle ( v ) ) ! = NULL ) ;
return cost ;
}
2007-07-01 19:24:54 +00:00
void Train : : Tick ( )
2004-08-09 17:04:08 +00:00
{
2007-07-01 19:24:54 +00:00
if ( _age_cargo_skip_counter = = 0 ) this - > cargo . AgeCargo ( ) ;
2004-08-09 17:04:08 +00:00
2007-07-01 19:24:54 +00:00
this - > tick_counter + + ;
2004-08-09 17:04:08 +00:00
2007-07-01 19:24:54 +00:00
if ( IsFrontEngine ( this ) ) {
this - > current_order_time + + ;
2007-06-20 19:17:22 +00:00
2007-07-01 19:24:54 +00:00
TrainLocoHandler ( this , false ) ;
2004-09-10 19:02:27 +00:00
2007-04-04 03:21:14 +00:00
/* make sure vehicle wasn't deleted. */
2007-07-01 19:24:54 +00:00
if ( this - > type = = VEH_TRAIN & & IsFrontEngine ( this ) )
TrainLocoHandler ( this , true ) ;
} else if ( IsFreeWagon ( this ) & & HASBITS ( this - > vehstatus , VS_CRASHED ) ) {
2007-04-04 03:21:14 +00:00
/* Delete flooded standalone wagon */
2007-08-03 19:36:00 +00:00
if ( + + this - > u . rail . crash_anim_pos > = 4400 ) delete this ;
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 )
{
2007-08-31 17:13:39 +00:00
if ( _patches . servint_trains = = 0 | | ! VehicleNeedsService ( v ) ) return ;
if ( v - > IsInDepot ( ) ) {
2006-09-03 11:49:38 +00:00
VehicleServiceInDepot ( v ) ;
return ;
}
2007-02-25 09:27:03 +00:00
TrainFindDepotData 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 ;
}
2007-02-25 09:27:03 +00:00
const Depot * 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
}
void OnNewDay_Train ( Vehicle * v )
{
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
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 */
2007-02-25 09:27:03 +00:00
if ( v - > current_order . type = = OT_GOTO_STATION ) {
TileIndex tile = GetStation ( v - > current_order . dest ) - > train_tile ;
if ( tile ! = 0 ) v - > dest_tile = tile ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
if ( ( v - > vehstatus & VS_STOPPED ) = = 0 ) {
/* running costs */
2007-08-29 21:27:16 +00:00
CommandCost cost ( v - > GetRunningCost ( ) / 364 ) ;
2004-08-09 17:04:08 +00:00
2007-06-18 19:53:50 +00:00
v - > profit_this_year - = cost . GetCost ( ) > > 8 ;
2004-08-09 17:04:08 +00:00
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
}
2007-06-22 22:42:18 +00:00
} else if ( IsTrainEngine ( v ) ) {
/* Also age engines that aren't front engines */
AgeVehicle ( v ) ;
2004-08-09 17:04:08 +00:00
}
}
2007-03-07 11:47:46 +00:00
void TrainsYearlyLoop ( )
2004-08-09 17:04:08 +00:00
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN & & IsFrontEngine ( v ) ) {
2007-04-04 03:21:14 +00:00
/* show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2004-08-09 17:04:08 +00:00
if ( _patches . train_income_warn & & v - > owner = = _local_player & & v - > age > = 730 & & v - > profit_this_year < 0 ) {
2007-06-21 17:25:17 +00:00
SetDParam ( 1 , v - > profit_this_year ) ;
2004-12-02 22:53:07 +00:00
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 ) ;
}
}
}
2007-03-07 11:47:46 +00:00
void InitializeTrains ( )
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
*/
2007-03-07 11:47:46 +00:00
void ConnectMultiheadedTrains ( )
2006-01-18 15:05:01 +00:00
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN ) {
2006-01-18 15:05:01 +00:00
v - > u . rail . other_multiheaded_part = NULL ;
}
}
FOR_ALL_VEHICLES ( v ) {
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN & & IsFrontEngine ( v ) ) {
2006-01-18 15:05:01 +00:00
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 - - ;
}
2007-02-25 09:27:03 +00:00
Vehicle * w ;
2007-08-30 13:03:56 +00:00
for ( w = u - > Next ( ) ; w ! = NULL & & ( w - > engine_type ! = u - > engine_type | | w - > u . rail . other_multiheaded_part ! = NULL ) ; w = GetNextVehicle ( w ) ) ;
2007-02-25 09:27:03 +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 + + ;
2006-01-18 15:05:01 +00:00
}
2007-02-25 09:27:03 +00:00
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 ) ;
2006-01-18 15:05:01 +00:00
}
}
} END_ENUM_WAGONS ( u )
}
}
}
2007-04-04 03:21:14 +00:00
/**
2006-01-18 15:05:01 +00:00
* 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
*/
2007-03-07 11:47:46 +00:00
void ConvertOldMultiheadToNew ( )
2006-01-18 15:05:01 +00:00
{
Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
2007-03-08 16:27:54 +00:00
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 ) {
2007-03-08 16:27:54 +00:00
if ( v - > type = = VEH_TRAIN ) {
2006-01-18 15:05:01 +00:00
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 */
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_MULTIHEAD ) SetMultiheaded ( u ) ;
2006-02-13 21:15:00 +00:00
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 ;
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_WAGON ) {
2006-02-13 21:15:00 +00:00
// normal wagon
SetTrainWagon ( u ) ;
break ;
}
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_MULTIHEAD & & rvi - > image_index = = u - > spritenum - 1 ) {
2006-01-18 15:05:01 +00:00
// rear end of a multiheaded engine
SetMultiheaded ( u ) ;
break ;
}
2007-01-30 11:53:35 +00:00
if ( rvi - > railveh_type = = RAILVEH_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
}
}
}
}