2005-07-24 14:12:37 +00:00
/* $Id$ */
2008-05-06 15:11:33 +00:00
/** @file train_cmd.cpp Handling of trains. */
2007-04-04 03:21:14 +00:00
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"
2007-12-21 22:50:51 +00:00
# include "tile_cmd.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"
2006-03-06 20:55:24 +00:00
# include "tunnel_map.h"
2007-06-01 11:41:02 +00:00
# include "articulated_vehicles.h"
2007-12-21 21:50:46 +00:00
# include "command_func.h"
2004-08-09 17:04:08 +00:00
# include "pathfind.h"
2005-01-31 11:23:10 +00:00
# include "npf.h"
2008-03-31 00:06:17 +00:00
# include "station_base.h"
2008-03-28 08:53:36 +00:00
# include "news_func.h"
2008-03-31 00:17:39 +00:00
# include "engine_func.h"
2008-04-29 21:31:29 +00:00
# include "engine_base.h"
2008-09-30 20:51:04 +00:00
# include "company_func.h"
# include "company_base.h"
2008-04-17 19:10:30 +00:00
# include "depot_base.h"
# include "depot_func.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"
2007-12-18 19:52:14 +00:00
# include "direction_func.h"
2006-05-27 16:12:16 +00:00
# include "yapf/yapf.h"
2008-08-02 22:53:51 +00:00
# include "yapf/follow_track.hpp"
2007-02-20 22:09:21 +00:00
# include "cargotype.h"
2007-05-19 09:40:18 +00:00
# include "group.h"
2007-10-28 15:40:18 +00:00
# include "table/sprites.h"
2007-12-16 15:38:51 +00:00
# include "tunnelbridge_map.h"
2007-12-21 19:49:27 +00:00
# include "strings_func.h"
2007-12-25 11:26:07 +00:00
# include "functions.h"
# include "window_func.h"
2007-12-26 13:50:40 +00:00
# include "date_func.h"
2007-12-27 13:35:39 +00:00
# include "vehicle_func.h"
2007-12-29 09:24:26 +00:00
# include "sound_func.h"
2008-01-09 23:00:59 +00:00
# include "signal_func.h"
2008-01-07 00:57:19 +00:00
# include "variables.h"
2008-01-07 09:19:53 +00:00
# include "autoreplace_gui.h"
2008-01-09 09:45:45 +00:00
# include "gfx_func.h"
2009-01-12 17:11:45 +00:00
# include "ai/ai.hpp"
2008-01-13 14:37:30 +00:00
# include "settings_type.h"
2008-03-30 23:24:18 +00:00
# include "order_func.h"
2008-04-19 23:19:12 +00:00
# include "newgrf_station.h"
2008-04-20 11:12:07 +00:00
# include "effectvehicle_func.h"
2008-07-24 15:19:26 +00:00
# include "gamelog.h"
# include "network/network.h"
2008-08-02 22:53:37 +00:00
# include "pbs.h"
2007-12-16 15:38:51 +00:00
2008-01-13 01:21:35 +00:00
# include "table/strings.h"
# include "table/train_cmd.h"
2004-08-09 17:04:08 +00:00
2009-01-10 00:31:47 +00:00
static Track ChooseTrainTrack ( Vehicle * v , TileIndex tile , DiagDirection enterdir , TrackBits tracks , bool force_res , bool * got_reservation , bool mark_stuck ) ;
2004-12-21 16:00:14 +00:00
static bool TrainCheckIfLineEnds ( Vehicle * v ) ;
2008-03-19 20:42:05 +00:00
static void TrainController ( Vehicle * v , Vehicle * nomove , bool update_image ) ;
2008-01-17 17:57:39 +00:00
static TileIndex TrainApproachingCrossingTile ( const Vehicle * v ) ;
2008-08-02 22:56:50 +00:00
static void CheckIfTrainNeedsService ( Vehicle * v ) ;
2008-08-02 22:56:07 +00:00
static void CheckNextTrainTile ( Vehicle * v ) ;
2004-08-09 17:04:08 +00:00
2006-08-22 14:38:37 +00:00
static const byte _vehicle_initial_x_fract [ 4 ] = { 10 , 8 , 4 , 8 } ;
static const byte _vehicle_initial_y_fract [ 4 ] = { 8 , 4 , 8 , 10 } ;
2008-01-11 15:10:59 +00:00
/**
* Determine the side in which the train will leave the tile
*
* @ param direction vehicle direction
* @ param track vehicle track bits
* @ return side of tile the train will leave
*/
static inline DiagDirection TrainExitDir ( Direction direction , TrackBits track )
{
static const TrackBits state_dir_table [ DIAGDIR_END ] = { TRACK_BIT_RIGHT , TRACK_BIT_LOWER , TRACK_BIT_LEFT , TRACK_BIT_UPPER } ;
DiagDirection diagdir = DirToDiagDir ( direction ) ;
/* Determine the diagonal direction in which we will exit this tile */
if ( ! HasBit ( direction , 0 ) & & track ! = state_dir_table [ diagdir ] ) {
diagdir = ChangeDiagDir ( diagdir , DIAGDIRDIFF_90LEFT ) ;
}
return diagdir ;
}
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 ;
2008-05-29 15:13:28 +00:00
return _settings_game . vehicle . freight_trains ;
2006-11-27 23:11:56 +00:00
}
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 .
*/
2008-04-07 12:36:50 +00:00
void TrainPowerChanged ( Vehicle * v )
2006-03-29 16:30:26 +00:00
{
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 ( ) ) {
2007-02-27 23:36:28 +00:00
RailType railtype = GetRailType ( u - > tile ) ;
2006-03-29 16:30:26 +00:00
2008-07-29 22:19:37 +00:00
/* Power is not added for articulated parts */
if ( ! IsArticulatedPart ( u ) ) {
bool engine_has_power = HasPowerOnRail ( u - > u . rail . railtype , railtype ) ;
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
2006-03-29 16:30:26 +00:00
2008-07-29 22:19:37 +00:00
if ( engine_has_power ) {
uint16 power = GetVehicleProperty ( u , 0x0B , rvi_u - > power ) ;
if ( power ! = 0 ) {
/* Halve power for multiheaded parts */
if ( IsMultiheaded ( u ) ) power / = 2 ;
2008-05-13 20:39:57 +00:00
2008-07-29 22:19:37 +00:00
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 ;
}
2007-04-18 18:37:40 +00:00
}
2006-12-28 13:18:07 +00:00
}
2008-07-29 22:19:37 +00:00
if ( HasBit ( u - > u . rail . flags , VRF_POWEREDWAGON ) & & HasPowerOnRail ( v - > u . rail . railtype , railtype ) ) {
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 ) ;
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
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 .
*/
2008-04-07 12:36:50 +00:00
static void TrainCargoChanged ( Vehicle * v )
2006-12-28 13:18:07 +00:00
{
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 ) ;
2008-07-29 22:19:37 +00:00
}
2006-12-28 13:18:07 +00:00
2008-07-29 22:19:37 +00:00
/* powered wagons have extra weight added */
if ( HasBit ( u - > u . rail . flags , VRF_POWEREDWAGON ) ) {
vweight + = RailVehInfo ( u - > u . rail . first_engine ) - > pow_wag_weight ;
2006-12-28 13:18:07 +00:00
}
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 ) ;
}
2008-07-24 15:19:26 +00:00
/** Logs a bug in GRF and shows a warning message if this
* is for the first time this happened .
* @ param u first vehicle of chain
*/
static void RailVehicleLengthChanged ( const Vehicle * u )
{
/* show a warning once for each engine in whole game and once for each GRF after each game load */
const Engine * engine = GetEngine ( u - > engine_type ) ;
uint32 grfid = engine - > grffile - > grfid ;
GRFConfig * grfconfig = GetGRFConfig ( grfid ) ;
if ( GamelogGRFBugReverse ( grfid , engine - > internal_id ) | | ! HasBit ( grfconfig - > grf_bugs , GBUG_VEH_LENGTH ) ) {
SetBit ( grfconfig - > grf_bugs , GBUG_VEH_LENGTH ) ;
SetDParamStr ( 0 , grfconfig - > name ) ;
SetDParam ( 1 , u - > engine_type ) ;
ShowErrorMessage ( STR_NEWGRF_BROKEN_VEHICLE_LENGTH , STR_NEWGRF_BROKEN , 0 , 0 ) ;
/* debug output */
char buffer [ 512 ] ;
SetDParamStr ( 0 , grfconfig - > name ) ;
GetString ( buffer , STR_NEWGRF_BROKEN , lastof ( buffer ) ) ;
DEBUG ( grf , 0 , " %s " , buffer + 3 ) ;
SetDParam ( 1 , u - > engine_type ) ;
GetString ( buffer , STR_NEWGRF_BROKEN_VEHICLE_LENGTH , lastof ( buffer ) ) ;
DEBUG ( grf , 0 , " %s " , buffer + 3 ) ;
if ( ! _networking ) _pause_game = - 1 ;
}
}
/** Checks if lengths of all rail vehicles are valid. If not, shows an error message. */
void CheckTrainsLengths ( )
{
const Vehicle * v ;
FOR_ALL_VEHICLES ( v ) {
if ( v - > type = = VEH_TRAIN & & v - > First ( ) = = v & & ! ( v - > vehstatus & VS_CRASHED ) ) {
for ( const Vehicle * u = v , * w = v - > Next ( ) ; w ! = NULL ; u = w , w = w - > Next ( ) ) {
if ( u - > u . rail . track ! = TRACK_BIT_DEPOT ) {
if ( ( w - > u . rail . track ! = TRACK_BIT_DEPOT & &
max ( abs ( u - > x_pos - w - > x_pos ) , abs ( u - > y_pos - w - > y_pos ) ) ! = u - > u . rail . cached_veh_length ) | |
( w - > u . rail . track = = TRACK_BIT_DEPOT & & TicksToLeaveDepot ( u ) < = 0 ) ) {
SetDParam ( 0 , v - > index ) ;
SetDParam ( 1 , v - > owner ) ;
ShowErrorMessage ( INVALID_STRING_ID , STR_BROKEN_VEHICLE_LENGTH , 0 , 0 ) ;
2008-07-24 16:23:55 +00:00
2008-07-24 15:19:26 +00:00
if ( ! _networking ) _pause_game = - 1 ;
}
}
}
}
}
}
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 .
2008-07-24 15:19:26 +00:00
* @ param same_length should length of vehicles stay the same ?
2005-06-05 15:37:00 +00:00
*/
2008-07-24 15:19:26 +00:00
void TrainConsistChanged ( Vehicle * v , bool same_length )
2005-11-14 19:48:04 +00:00
{
2008-04-07 12:36:50 +00:00
uint16 max_speed = UINT16_MAX ;
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 ;
2008-01-09 21:05:03 +00:00
v - > u . rail . compatible_railtypes = RAILTYPES_NONE ;
2005-06-05 15:37:00 +00:00
2008-01-01 22:24:18 +00:00
bool train_can_tilt = true ;
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
2008-06-13 13:28:55 +00:00
if ( IsTrainEngine ( u ) ) first_engine = u - > engine_type ;
2008-04-23 21:55:24 +00:00
/* Set user defined data to its default value */
u - > u . rail . user_def_data = rvi_u - > user_def_data ;
}
for ( Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
/* Update user defined data (must be done before other properties) */
u - > u . rail . user_def_data = GetVehicleProperty ( u , 0x25 , u - > u . rail . user_def_data ) ;
}
for ( Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
const RailVehicleInfo * rvi_u = RailVehInfo ( u - > engine_type ) ;
if ( ! HasBit ( EngInfo ( u - > engine_type ) - > misc_flags , EF_RAIL_TILTS ) ) train_can_tilt = false ;
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 ) ;
2007-10-28 15:40:18 +00:00
/* Reset color map */
u - > colormap = PAL_NONE ;
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 ;
}
}
2008-07-29 22:19:37 +00:00
/* Check powered wagon / visual effect callback */
if ( HasBit ( EngInfo ( u - > engine_type ) - > callbackmask , CBM_TRAIN_WAGON_POWER ) ) {
uint16 callback = GetVehicleCallback ( CBID_TRAIN_WAGON_POWER , 0 , 0 , u - > engine_type , u ) ;
2005-06-06 00:19:24 +00:00
2008-07-29 22:19:37 +00:00
if ( callback ! = CALLBACK_FAILED ) u - > u . rail . cached_vis_effect = GB ( callback , 0 , 8 ) ;
}
2006-05-31 18:00:08 +00:00
2008-07-29 22:19:37 +00:00
if ( rvi_v - > pow_wag_power ! = 0 & & rvi_u - > railveh_type = = RAILVEH_WAGON & &
UsesWagonOverride ( u ) & & ! HasBit ( u - > u . rail . cached_vis_effect , 7 ) ) {
/* wagon is powered */
SetBit ( u - > u . rail . flags , VRF_POWEREDWAGON ) ; // cache 'powered' status
} else {
ClrBit ( u - > u . rail . flags , VRF_POWEREDWAGON ) ;
}
2005-06-06 00:19:24 +00:00
2008-07-29 22:19:37 +00:00
if ( ! IsArticulatedPart ( u ) ) {
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 */
2007-11-19 21:02:30 +00:00
if ( HasBit ( u - > u . rail . flags , VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL ) ) {
2006-11-17 19:31:44 +00:00
u - > u . rail . railtype = RAILTYPE_RAIL ;
2008-01-09 21:05:03 +00:00
u - > u . rail . compatible_railtypes | = RAILTYPES_RAIL ;
2006-11-17 19:31:44 +00:00
}
2007-04-04 03:21:14 +00:00
/* max speed is the minimum of the speed limits of all vehicles in the consist */
2008-05-29 15:13:28 +00:00
if ( ( rvi_u - > railveh_type ! = RAILVEH_WAGON | | _settings_game . vehicle . wagon_speed_limits ) & & ! UsesWagonOverride ( u ) ) {
2007-04-18 18:37:40 +00:00
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 ;
2007-11-19 21:02:30 +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 ;
2008-07-24 15:19:26 +00:00
veh_len = 8 - 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
/* verify length hasn't changed */
if ( same_length & & veh_len ! = u - > u . rail . cached_veh_length ) RailVehicleLengthChanged ( u ) ;
/* update vehicle length? */
if ( ! same_length ) u - > u . rail . cached_veh_length = 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 ;
2008-01-01 22:24:18 +00:00
v - > u . rail . cached_tilt = train_can_tilt ;
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 ) ;
2008-03-17 21:50:53 +00:00
2008-03-17 22:13:00 +00:00
if ( IsFrontEngine ( v ) ) {
UpdateTrainAcceleration ( v ) ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
}
2005-06-05 15:37:00 +00:00
}
2004-08-09 17:04:08 +00:00
2005-01-26 12:51:04 +00:00
enum AccelType {
AM_ACCEL ,
AM_BRAKE
} ;
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 )
{
2008-04-07 12:36:50 +00:00
static const int absolute_max_speed = UINT16_MAX ;
2008-01-01 14:00:31 +00:00
int max_speed = absolute_max_speed ;
2007-09-10 17:13:37 +00:00
int speed = v - > cur_speed * 10 / 16 ; // km-ish/h -> mp/h
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 + + ) {
2008-01-01 18:12:17 +00:00
Direction this_dir = u - > direction ;
Direction next_dir = u - > Next ( ) - > direction ;
DirDiff dirdiff = DirDifference ( this_dir , next_dir ) ;
if ( dirdiff = = DIRDIFF_SAME ) continue ;
if ( dirdiff = = DIRDIFF_45LEFT ) curvecount [ 0 ] + + ;
if ( dirdiff = = DIRDIFF_45RIGHT ) curvecount [ 1 ] + + ;
if ( dirdiff = = DIRDIFF_45LEFT | | dirdiff = = DIRDIFF_45RIGHT ) {
if ( lastpos ! = - 1 ) {
numcurve + + ;
sum + = pos - lastpos ;
if ( pos - lastpos = = 1 ) {
max_speed = 88 ;
2005-01-26 12:51:04 +00:00
}
}
2008-01-01 18:12:17 +00:00
lastpos = pos ;
2005-01-26 12:51:04 +00:00
}
2007-04-04 03:21:14 +00:00
/*if we have a 90 degree turn, fix the speed limit to 60 */
2008-01-01 18:12:17 +00:00
if ( dirdiff = = DIRDIFF_90LEFT | | dirdiff = = DIRDIFF_90RIGHT ) {
2005-01-26 12:51:04 +00:00
max_speed = 61 ;
}
}
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 ) {
2008-01-01 14:00:31 +00:00
max_speed = absolute_max_speed ;
2005-01-26 12:51:04 +00:00
} else if ( total > 1 ) {
2008-01-01 18:12:17 +00:00
if ( numcurve > 0 ) sum / = numcurve ;
2007-11-19 18:38:10 +00:00
max_speed = 232 - ( 13 - Clamp ( sum , 1 , 12 ) ) * ( 13 - Clamp ( sum , 1 , 12 ) ) ;
2005-01-26 12:51:04 +00:00
}
}
2008-01-01 14:00:31 +00:00
if ( max_speed ! = absolute_max_speed ) {
/* Apply the engine's rail type curve speed advantage, if it slowed by curves */
const RailtypeInfo * rti = GetRailTypeInfo ( v - > u . rail . railtype ) ;
max_speed + = ( max_speed / 2 ) * rti - > curve_speed ;
2008-01-01 22:24:18 +00:00
if ( v - > u . rail . cached_tilt ) {
/* Apply max_speed bonus of 20% for a tilting train */
max_speed + = max_speed / 5 ;
}
2008-01-01 14:00:31 +00:00
}
2005-01-26 12:51:04 +00:00
2005-11-18 23:41:03 +00:00
if ( IsTileType ( v - > tile , MP_STATION ) & & IsFrontEngine ( v ) ) {
2008-04-05 15:30:15 +00:00
if ( v - > current_order . ShouldStopAtStation ( v , GetStationIndex ( 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
2008-01-01 14:15:28 +00:00
int st_max_speed = 120 ;
2005-01-26 12:51:04 +00:00
2008-01-01 14:15:28 +00:00
int delta_v = v - > cur_speed / ( station_length + 1 ) ;
if ( v - > max_speed > ( v - > cur_speed - delta_v ) ) {
st_max_speed = v - > cur_speed - ( delta_v / 10 ) ;
}
2005-01-26 12:51:04 +00:00
2008-01-01 14:15:28 +00:00
st_max_speed = max ( st_max_speed , 25 * station_length ) ;
max_speed = min ( max_speed , st_max_speed ) ;
2005-01-26 12:51:04 +00:00
}
}
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
2007-11-19 21:02:30 +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
2007-11-19 21:02:30 +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
}
}
2008-04-07 12:36:50 +00:00
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 ) ;
2007-11-19 18:38:10 +00:00
v - > acceleration = Clamp ( power / weight * 4 , 1 , 255 ) ;
2004-08-09 17:04:08 +00:00
}
2008-04-21 20:50:58 +00:00
SpriteID Train : : GetImage ( Direction direction ) const
2004-08-09 17:04:08 +00:00
{
2008-04-21 20:50:58 +00:00
uint8 spritenum = this - > spritenum ;
SpriteID sprite ;
2004-08-09 17:04:08 +00:00
2007-11-19 21:02:30 +00:00
if ( HasBit ( this - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) direction = ReverseDir ( direction ) ;
2006-03-18 13:00:32 +00:00
2008-04-21 20:50:58 +00:00
if ( is_custom_sprite ( spritenum ) ) {
sprite = GetCustomVehicleSprite ( this , ( Direction ) ( direction + 4 * IS_CUSTOM_SECONDHEAD_SPRITE ( spritenum ) ) ) ;
if ( sprite ! = 0 ) return sprite ;
2008-04-29 21:31:29 +00:00
spritenum = GetEngine ( this - > engine_type ) - > image_index ;
2004-08-09 17:04:08 +00:00
}
2004-09-10 19:02:27 +00:00
2008-04-21 20:50:58 +00:00
sprite = _engine_sprite_base [ spritenum ] + ( ( direction + _engine_sprite_add [ spritenum ] ) & _engine_sprite_and [ spritenum ] ) ;
if ( this - > cargo . Count ( ) > = this - > cargo_cap / 2U ) sprite + = _wagon_full_adder [ spritenum ] ;
2004-08-09 17:04:08 +00:00
2008-04-21 20:50:58 +00:00
return sprite ;
2004-08-09 17:04:08 +00:00
}
2008-04-21 20:50:58 +00:00
static SpriteID GetRailIcon ( EngineID engine , bool rear_head , int & y )
2004-08-09 17:04:08 +00:00
{
2008-04-21 20:50:58 +00:00
Direction dir = rear_head ? DIR_E : DIR_W ;
uint8 spritenum = RailVehInfo ( engine ) - > image_index ;
if ( is_custom_sprite ( spritenum ) ) {
SpriteID sprite = GetCustomVehicleIcon ( engine , dir ) ;
if ( sprite ! = 0 ) {
y + = _traininfo_vehicle_pitch ; // TODO Make this per-GRF
return sprite ;
2005-10-30 21:47:42 +00:00
}
2008-04-21 20:50:58 +00:00
2008-04-29 21:31:29 +00:00
spritenum = GetEngine ( engine ) - > image_index ;
2004-08-09 17:04:08 +00:00
}
2008-04-21 20:50:58 +00:00
if ( rear_head ) spritenum + + ;
return ( ( 6 + _engine_sprite_add [ spritenum ] ) & _engine_sprite_and [ spritenum ] ) + _engine_sprite_base [ spritenum ] ;
}
void DrawTrainEngine ( int x , int y , EngineID engine , SpriteID pal )
{
if ( RailVehInfo ( engine ) - > railveh_type = = RAILVEH_MULTIHEAD ) {
int yf = y ;
int yr = y ;
SpriteID spritef = GetRailIcon ( engine , false , yf ) ;
SpriteID spriter = GetRailIcon ( engine , true , yr ) ;
DrawSprite ( spritef , pal , x - 14 , yf ) ;
DrawSprite ( spriter , pal , x + 15 , yr ) ;
} else {
SpriteID sprite = GetRailIcon ( engine , false , y ) ;
DrawSprite ( sprite , 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
{
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( engine ) ;
2008-08-15 17:54:43 +00:00
CommandCost value ( EXPENSES_NEW_VEHICLES , ( GetEngineProperty ( engine , 0x17 , rvi - > cost_factor ) * _price . build_railwagon ) > > 8 ) ;
2004-08-09 17:04:08 +00:00
2007-09-17 04:23:03 +00:00
uint num_vehicles = 1 + CountArticulatedParts ( engine , false ) ;
2005-11-05 16:07:26 +00:00
2004-08-09 17:04:08 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
2008-03-28 22:59:43 +00:00
/* Check that the wagon can drive on the track in question */
if ( ! IsCompatibleRail ( rvi - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
2007-09-17 04:23:03 +00:00
/* Allow for the wagon and the articulated parts, plus one to "terminate" the list. */
2008-06-10 21:59:22 +00:00
Vehicle * * vl = AllocaM ( Vehicle * , num_vehicles + 1 ) ;
2007-09-17 04:23:03 +00:00
memset ( vl , 0 , sizeof ( * vl ) * ( num_vehicles + 1 ) ) ;
2006-10-12 15:03:19 +00:00
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 & &
2007-10-08 20:16:25 +00:00
IsFreeWagon ( w ) & & w - > engine_type = = engine & &
! HASBITS ( w - > vehstatus , VS_CRASHED ) ) { /// do not connect new wagon with crashed/flooded consists
2005-01-06 22:31:58 +00:00
u = GetLastVehicleInChain ( w ) ;
2004-08-09 17:04:08 +00:00
break ;
}
}
2007-10-21 16:45:00 +00:00
v = new ( v ) Train ( ) ;
2004-08-09 17:04:08 +00:00
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 ) ;
2008-09-30 20:39:50 +00:00
v - > owner = _current_company ;
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 ;
2008-04-07 12:36:50 +00:00
// v->subtype = 0;
2005-11-18 23:41:03 +00:00
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 ;
2008-04-07 12:36:50 +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 ) ;
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( v - > First ( ) , false ) ;
2007-08-30 21:11:12 +00:00
UpdateTrainGroupID ( v - > First ( ) ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
2008-09-30 20:39:50 +00:00
if ( IsLocalCompany ( ) ) {
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
}
2008-09-30 20:39:50 +00:00
GetCompany ( _current_company ) - > num_engines [ engine ] + + ;
2004-08-09 17:04:08 +00:00
}
}
2008-01-09 16:55:48 +00:00
return 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 */
2008-04-07 12:36:50 +00:00
static void NormalizeTrainVehInDepot ( const Vehicle * u )
2004-08-09 17:04:08 +00:00
{
2008-04-07 12:36:50 +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 ;
}
}
}
2008-04-07 12:36:50 +00:00
static CommandCost EstimateTrainCost ( EngineID engine , const RailVehicleInfo * rvi )
2004-08-09 17:04:08 +00:00
{
2008-08-15 17:54:43 +00:00
return CommandCost ( EXPENSES_NEW_VEHICLES , GetEngineProperty ( engine , 0x17 , rvi - > cost_factor ) * ( _price . build_railvehicle > > 3 ) > > 5 ) ;
2004-08-09 17:04:08 +00:00
}
2008-04-07 12:36: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 ;
2008-04-07 12:36:50 +00:00
// u->subtype = 0;
2005-11-18 23:41:03 +00:00
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
2008-07-09 20:55:47 +00:00
* @ param p2 bit 1 prevents any free cars from being added to the train
2004-08-09 17:04:08 +00:00
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdBuildRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
2004-08-09 17:04:08 +00:00
{
2008-09-30 20:39:50 +00:00
/* Check if the engine-type is valid (for the company) */
if ( ! IsEngineBuildable ( p1 , VEH_TRAIN , _current_company ) ) 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
2008-09-30 20:39:50 +00:00
* to the company . Doesn ' t matter if only the cost is queried */
2005-03-06 16:58:42 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
2008-04-17 18:24:45 +00:00
if ( ! IsRailDepotTile ( tile ) ) return CMD_ERROR ;
2008-09-30 20:39:50 +00:00
if ( ! IsTileOwner ( tile , _current_company ) ) return CMD_ERROR ;
2005-03-06 16:58:42 +00:00
}
2005-01-29 23:58:07 +00:00
2007-02-25 09:27:03 +00:00
const RailVehicleInfo * rvi = RailVehInfo ( p1 ) ;
2005-07-31 13:08:08 +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 ) +
2007-09-17 04:23:03 +00:00
CountArticulatedParts ( p1 , false ) ;
2004-08-09 17:04:08 +00:00
if ( ! ( flags & DC_QUERY_COST ) ) {
2008-03-28 22:59:43 +00:00
/* Check if depot and new engine uses the same kind of tracks *
* We need to see if the engine got power on the tile to avoid eletric engines in non - electric depots */
if ( ! HasPowerOnRail ( rvi - > railtype , GetRailType ( tile ) ) ) return CMD_ERROR ;
2007-09-17 04:23:03 +00:00
/* Allow for the dual-heads and the articulated parts, plus one to "terminate" the list. */
2008-06-10 21:59:22 +00:00
Vehicle * * vl = AllocaM ( Vehicle * , num_vehicles + 1 ) ;
2007-09-17 04:23:03 +00:00
memset ( vl , 0 , sizeof ( * vl ) * ( num_vehicles + 1 ) ) ;
2006-10-12 15:03:19 +00:00
2008-04-07 12:36:50 +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 ) ;
2008-04-07 12:36:50 +00:00
}
2004-08-09 17:04:08 +00:00
2007-02-25 09:27:03 +00:00
Vehicle * v = vl [ 0 ] ;
2005-11-05 16:07:26 +00:00
2008-07-09 20:55:47 +00:00
UnitID unit_num = ( flags & DC_AUTOREPLACE ) ? 0 : GetFreeUnitNumber ( VEH_TRAIN ) ;
2008-05-29 15:13:28 +00:00
if ( unit_num > _settings_game . vehicle . max_trains )
2006-05-11 13:31:14 +00:00
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
2007-10-21 16:52:01 +00:00
v = new ( v ) Train ( ) ;
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 ;
2008-09-30 20:39:50 +00:00
v - > owner = _current_company ;
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 ) ;
2008-04-07 12:36:50 +00:00
// v->running_ticks = 0;
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 ;
2008-04-07 12:36:50 +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 ;
2008-04-07 12:36:50 +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
2008-01-12 19:58:06 +00:00
v - > name = NULL ;
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
2008-05-29 15:13:28 +00:00
v - > service_interval = _settings_game . vehicle . servint_trains ;
2004-08-09 17:04:08 +00:00
v - > date_of_last_service = _date ;
2006-08-20 19:05:28 +00:00
v - > build_year = _cur_year ;
2004-08-09 17:04:08 +00:00
v - > cur_image = 0xAC2 ;
2005-12-28 22:29:59 +00:00
v - > random_bits = VehicleRandomBits ( ) ;
2004-08-09 17:04:08 +00:00
2008-04-07 12:36:50 +00:00
// v->vehicle_flags = 0;
2007-11-20 13:35:54 +00:00
if ( e - > flags & ENGINE_EXCLUSIVE_PREVIEW ) SetBit ( v - > vehicle_flags , VF_BUILT_AS_PROTOTYPE ) ;
2007-02-28 17:59:05 +00:00
2007-05-19 09:40:18 +00:00
v - > group_id = DEFAULT_GROUP ;
2008-04-07 12:36:50 +00:00
// v->subtype = 0;
2005-11-18 23:41:03 +00:00
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
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( v , false ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( v ) ;
2005-07-31 13:08:08 +00:00
2008-07-09 20:55:47 +00:00
if ( ! HasBit ( p2 , 1 ) & & ! ( flags & DC_AUTOREPLACE ) ) { // 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 ) ;
2008-05-18 16:51:44 +00:00
InvalidateWindowClassesData ( WC_TRAINS_LIST , 0 ) ;
2004-08-09 17:04:08 +00:00
InvalidateWindow ( WC_COMPANY , v - > owner ) ;
2008-09-30 20:39:50 +00:00
if ( IsLocalCompany ( ) ) {
2007-08-07 23:07:10 +00:00
InvalidateAutoreplaceWindow ( v - > engine_type , v - > group_id ) ; // updates the replace Train window
2008-04-07 12:36:50 +00:00
}
2007-02-06 11:11:12 +00:00
2008-09-30 20:39:50 +00:00
GetCompany ( _current_company ) - > 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 */
2008-04-17 18:24:45 +00:00
if ( ! IsRailDepotTile ( tile ) | | 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 ) ;
2008-12-31 17:52:42 +00:00
/* First can be an articulated engine, meaning GetNextVehicle() isn't
* v - > Next ( ) . Thus set the next vehicle of the last articulated part
* and the last articulated part is just before the next vehicle ( v ) . */
v - > Previous ( ) - > SetNext ( NULL ) ;
2005-11-18 23:41:03 +00:00
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-10-08 20:16:25 +00:00
if ( dst - > type = = VEH_TRAIN & & IsFreeWagon ( dst ) & & dst - > tile = = tile & & ! HASBITS ( dst - > vehstatus , VS_CRASHED ) ) {
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-12-04 15:32:54 +00:00
v - > SetNext ( NULL ) ;
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
2008-08-16 14:02:20 +00:00
* Note : DC_AUTOREPLACE is set when autoreplace tries to undo its modifications or moves vehicles to temporary locations inside the depot .
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
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdMoveRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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-10-08 20:16:25 +00:00
/* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
if ( HASBITS ( src - > vehstatus , VS_CRASHED ) ) return CMD_ERROR ;
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 ;
2007-10-08 20:16:25 +00:00
/* Do not allow appending to crashed vehicles, too */
if ( HASBITS ( dst - > vehstatus , VS_CRASHED ) ) 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-11-19 21:02:30 +00:00
if ( HasBit ( p2 , 0 ) & & src_head = = dst_head ) return CommandCost ( ) ;
2006-01-25 21:33:57 +00:00
2008-08-16 14:02:20 +00:00
/* check if all vehicles in the source train are stopped inside a depot. */
int src_len = CheckTrainStoppedInDepot ( src_head ) ;
if ( src_len < 0 ) return_cmd_error ( STR_881A_TRAINS_CAN_ONLY_BE_ALTERED ) ;
2004-08-09 17:04:08 +00:00
2008-08-16 14:02:20 +00:00
if ( ( flags & DC_AUTOREPLACE ) = = 0 ) {
/* Check whether there are more than 'max_len' train units (articulated parts and rear heads do not count) in the new chain */
int max_len = _settings_game . vehicle . mammoth_trains ? 100 : 10 ;
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 . */
2007-11-19 21:02:30 +00:00
if ( HasBit ( p2 , 0 ) ) {
2006-01-25 21:33:57 +00:00
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 ) ) {
2008-08-16 14:02:20 +00:00
UnitID unit_num = ( ( flags & DC_AUTOREPLACE ) ! = 0 ? 0 : GetFreeUnitNumber ( VEH_TRAIN ) ) ;
2008-05-29 15:13:28 +00:00
if ( unit_num > _settings_game . vehicle . max_trains )
2004-08-09 17:04:08 +00:00
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
}
2008-09-22 19:28:53 +00:00
/* When we move the front vehicle, the second vehicle might need a unitnumber */
2008-09-26 19:01:57 +00:00
if ( ! HasBit ( p2 , 0 ) & & ( IsFreeWagon ( src ) | | ( IsFrontEngine ( src ) & & dst = = NULL ) ) & & ( flags & DC_AUTOREPLACE ) = = 0 ) {
2008-09-22 19:28:53 +00:00
Vehicle * second = GetNextUnit ( src ) ;
if ( second ! = NULL & & IsTrainEngine ( second ) & & GetFreeUnitNumber ( VEH_TRAIN ) > _settings_game . vehicle . max_trains ) {
return_cmd_error ( STR_00E1_TOO_MANY_VEHICLES_IN_GAME ) ;
}
}
2007-12-30 21:35:48 +00:00
/*
* Check whether the vehicles in the source chain are in the destination
* chain . This can easily be done by checking whether the first vehicle
* of the source chain is in the destination chain as the Next / Previous
* pointers always make a doubly linked list of it where the assumption
* v - > Next ( ) - > Previous ( ) = = v holds ( assuming v - > Next ( ) ! = NULL ) .
*/
bool src_in_dst = false ;
for ( Vehicle * v = dst_head ; ! src_in_dst & & v ! = NULL ; v = v - > Next ( ) ) src_in_dst = v = = src ;
/*
* If the source chain is in the destination chain then the user is
* only reordering the vehicles , thus not attaching a new vehicle .
* Therefor the ' allow wagon attach ' callback does not need to be
* called . If it would be called strange things would happen because
* one ' attaches ' an already ' attached ' vehicle causing more trouble
* than it actually solves ( infinite loops and such ) .
*/
2008-08-16 14:02:20 +00:00
if ( dst_head ! = NULL & & ! src_in_dst & & ( flags & DC_AUTOREPLACE ) = = 0 ) {
2007-12-30 21:35:48 +00:00
/*
* When performing the ' allow wagon attach ' callback , we have to check
* that for each and every wagon , not only the first one . This means
* that we have to test one wagon , attach it to the train and then test
* the next wagon till we have reached the end . We have to restore it
* to the state it was before we ' tried ' attaching the train when the
* attaching fails or succeeds because we are not ' only ' doing this
* in the DC_EXEC state .
*/
Vehicle * dst_tail = dst_head ;
while ( dst_tail - > Next ( ) ! = NULL ) dst_tail = dst_tail - > Next ( ) ;
Vehicle * orig_tail = dst_tail ;
Vehicle * next_to_attach = src ;
Vehicle * src_previous = src - > Previous ( ) ;
while ( next_to_attach ! = NULL ) {
2008-09-26 06:52:06 +00:00
/* Don't check callback for articulated or rear dual headed parts */
if ( ! IsArticulatedPart ( next_to_attach ) & & ! IsRearDualheaded ( next_to_attach ) ) {
/* Back up and clear the first_engine data to avoid using wagon override group */
EngineID first_engine = next_to_attach - > u . rail . first_engine ;
next_to_attach - > u . rail . first_engine = INVALID_ENGINE ;
uint16 callback = GetVehicleCallbackParent ( CBID_TRAIN_ALLOW_WAGON_ATTACH , 0 , 0 , dst_head - > engine_type , next_to_attach , dst_head ) ;
/* Restore original first_engine data */
next_to_attach - > u . rail . first_engine = first_engine ;
if ( callback ! = CALLBACK_FAILED ) {
StringID error = STR_NULL ;
if ( callback = = 0xFD ) error = STR_INCOMPATIBLE_RAIL_TYPES ;
if ( callback < 0xFD ) error = GetGRFStringID ( GetEngineGRFID ( dst_head - > engine_type ) , 0xD000 + callback ) ;
if ( error ! = STR_NULL ) {
/*
* The attaching is not allowed . In this case ' next_to_attach '
* can contain some vehicles of the ' source ' and the destination
* train can have some too . We ' just ' add the to - be added wagons
* to the chain and then split it where it was previously
* separated , i . e . the tail of the original destination train .
* Furthermore the ' previous ' link of the original source vehicle needs
* to be restored , otherwise the train goes missing in the depot .
*/
dst_tail - > SetNext ( next_to_attach ) ;
orig_tail - > SetNext ( NULL ) ;
if ( src_previous ! = NULL ) src_previous - > SetNext ( src ) ;
return_cmd_error ( error ) ;
}
2007-12-30 21:35:48 +00:00
}
2006-05-02 21:43:47 +00:00
}
2007-12-30 21:35:48 +00:00
2007-12-31 21:51:16 +00:00
/* Only check further wagons if told to move the chain */
if ( ! HasBit ( p2 , 0 ) ) break ;
2007-12-30 21:35:48 +00:00
/*
* Adding a next wagon to the chain so we can test the other wagons .
* First ' take ' the first wagon from ' next_to_attach ' and move it
* to the next wagon . Then add that to the tail of the destination
* train and update the tail with the new vehicle .
*/
Vehicle * to_add = next_to_attach ;
next_to_attach = next_to_attach - > Next ( ) ;
to_add - > SetNext ( NULL ) ;
dst_tail - > SetNext ( to_add ) ;
dst_tail = dst_tail - > Next ( ) ;
2006-05-02 21:43:47 +00:00
}
2007-12-30 21:35:48 +00:00
/*
* When we reach this the attaching is allowed . It also means that the
* chain of vehicles to attach is empty , so we do not need to merge that .
* This means only the splitting needs to be done .
* Furthermore the ' previous ' link of the original source vehicle needs
* to be restored , otherwise the train goes missing in the depot .
*/
orig_tail - > SetNext ( NULL ) ;
if ( src_previous ! = NULL ) src_previous - > SetNext ( src ) ;
2006-05-02 21:43:47 +00:00
}
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
}
}
2007-11-19 21:02:30 +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 ) ;
2009-01-03 13:52:06 +00:00
assert ( src - > orders . list = = NULL ) ;
2007-05-19 09:40:18 +00:00
2008-04-07 12:36:50 +00:00
/* Decrease the engines number of the src engine_type */
2007-05-19 09:40:18 +00:00
if ( ! IsDefaultGroupID ( src - > group_id ) & & IsValidGroupID ( src - > group_id ) ) {
GetGroup ( src - > group_id ) - > num_engines [ src - > engine_type ] - - ;
}
2008-04-07 12:36:50 +00:00
/* If we move an engine to a new line affect it to the DEFAULT_GROUP */
2007-05-19 09:40:18 +00:00
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 ) ;
2009-01-02 20:59:04 +00:00
DeleteWindowById ( WC_VEHICLE_ORDERS , src - > index ) ;
DeleteWindowById ( WC_VEHICLE_REFIT , src - > index ) ;
DeleteWindowById ( WC_VEHICLE_DETAILS , src - > index ) ;
DeleteWindowById ( WC_VEHICLE_TIMETABLE , 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 ;
2008-03-15 13:21:31 +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 ;
2008-12-28 14:37:19 +00:00
CmdMoveRailVehicle ( 0 , flags , src_head - > index | ( INVALID_VEHICLE < < 16 ) , 1 , text ) ;
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 ) ;
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( src_head , false ) ;
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
/* Update the refit button and window */
InvalidateWindow ( WC_VEHICLE_REFIT , src_head - > index ) ;
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , src_head - > index , VVW_WIDGET_REFIT_VEH ) ;
2005-06-06 14:26:15 +00:00
}
/* 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 ) ;
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( dst_head , false ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( dst_head ) ;
2005-11-18 23:41:03 +00:00
if ( IsFrontEngine ( dst_head ) ) {
2005-06-06 14:26:15 +00:00
/* Update the refit button and window */
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , dst_head - > index , VVW_WIDGET_REFIT_VEH ) ;
2005-06-06 14:26:15 +00:00
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
}
2008-05-18 16:51:44 +00:00
InvalidateWindowClassesData ( WC_TRAINS_LIST , 0 ) ;
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
/** 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
2008-04-18 04:37:06 +00:00
* if the wagon is dragged , don ' t delete the possibly belonging rear - engine to some front
2005-05-05 20:46:14 +00:00
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdSellRailWagon ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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 ;
2009-01-12 17:11:45 +00:00
if ( ! IsValidVehicleID ( p1 ) | | p2 > 1 ) 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
2007-10-08 20:06:37 +00:00
if ( HASBITS ( v - > vehstatus , VS_CRASHED ) ) return_cmd_error ( STR_CAN_T_SELL_DESTROYED_VEHICLE ) ;
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 ) ) {
2008-05-17 13:01:30 +00:00
DeleteWindowById ( WC_VEHICLE_VIEW , first - > index ) ;
2009-01-02 20:59:04 +00:00
DeleteWindowById ( WC_VEHICLE_ORDERS , first - > index ) ;
DeleteWindowById ( WC_VEHICLE_REFIT , first - > index ) ;
DeleteWindowById ( WC_VEHICLE_DETAILS , first - > index ) ;
DeleteWindowById ( WC_VEHICLE_TIMETABLE , first - > index ) ;
2005-11-08 23:18:09 +00:00
}
2005-05-05 20:46:14 +00:00
InvalidateWindow ( WC_VEHICLE_DEPOT , first - > tile ) ;
2008-05-18 16:51:44 +00:00
InvalidateWindowClassesData ( WC_TRAINS_LIST , 0 ) ;
2005-05-05 20:46:14 +00:00
}
2004-08-09 17:04:08 +00:00
2008-01-09 16:55:48 +00:00
CommandCost cost ( EXPENSES_NEW_VEHICLES ) ;
2005-05-05 20:46:14 +00:00
switch ( p2 ) {
2009-01-12 17:11:45 +00:00
case 0 : { /* Delete given wagon */
2005-05-05 20:46:14 +00:00
bool switch_engine = false ; // update second wagon to engine?
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 ) ;
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
2008-09-26 19:25:49 +00:00
/* 2. We are selling the front vehicle, some special action might be required
2005-11-18 23:41:03 +00:00
* here , so take attention */
2008-09-26 19:25:49 +00:00
if ( 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 ) ) {
2008-09-26 19:25:49 +00:00
if ( IsTrainEngine ( first ) ) {
/* Let the new front engine take over the setup of the old engine */
switch_engine = true ;
if ( flags & DC_EXEC ) {
/* Make sure the group counts stay correct. */
new_f - > group_id = first - > group_id ;
first - > group_id = DEFAULT_GROUP ;
/* Copy orders (by sharing) */
2009-01-03 13:52:06 +00:00
new_f - > orders . list = first - > orders . list ;
2008-09-26 19:25:49 +00:00
new_f - > AddToShared ( first ) ;
DeleteVehicleOrders ( first ) ;
/* Copy other important data from the front engine */
new_f - > CopyVehicleConfigAndStatistics ( first ) ;
/* If we deleted a window then open a new one for the 'new' train */
2008-09-30 20:39:50 +00:00
if ( IsLocalCompany ( ) & & w ! = NULL ) ShowVehicleViewWindow ( new_f ) ;
2008-09-26 19:25:49 +00:00
}
} else {
/* We are selling a free wagon, and construct a new train at the same time.
* This needs lots of extra checks ( e . g . train limit ) , which are done by first moving
* the remaining vehicles to a new row */
cost . AddCost ( DoCommand ( 0 , new_f - > index | INVALID_VEHICLE < < 16 , 1 , flags , CMD_MOVE_RAIL_VEHICLE ) ) ;
if ( cost . Failed ( ) ) return cost ;
}
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 ) ;
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
2008-04-07 12:36:50 +00:00
* 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 ) ;
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( first , false ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( first ) ;
2008-03-17 22:13:00 +00:00
if ( IsFrontEngine ( first ) ) InvalidateWindow ( WC_VEHICLE_REFIT , first - > index ) ;
2005-05-05 20:46:14 +00:00
}
2004-09-10 19:02:27 +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 */
2008-04-07 12:36:50 +00:00
for ( Vehicle * tmp ; 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 ) ;
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 ) ;
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 ) ;
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( first , false ) ;
2007-05-19 09:40:18 +00:00
UpdateTrainGroupID ( first ) ;
2005-11-18 23:41:03 +00:00
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 ) ;
2008-04-01 14:03:20 +00:00
this - > x_extent = GB ( x , 16 , 8 ) ;
this - > y_extent = GB ( x , 24 , 8 ) ;
this - > z_extent = 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 ) ;
}
2008-04-07 12:36:50 +00:00
static inline void SetLastSpeed ( Vehicle * v , int spd )
2005-11-14 19:48:04 +00:00
{
2004-08-09 17:04:08 +00:00
int old = v - > u . rail . last_speed ;
if ( spd ! = old ) {
v - > u . rail . last_speed = spd ;
2008-05-29 15:13:28 +00:00
if ( _settings_client . gui . vehicle_speed | | ( old = = 0 ) ! = ( spd = = 0 ) ) {
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
2008-04-07 12:36:50 +00:00
}
2004-08-09 17:04:08 +00:00
}
}
2008-08-02 22:53:21 +00:00
/** Mark a train as stuck and stop it if it isn't stopped right now. */
static void MarkTrainAsStuck ( Vehicle * v )
2005-01-09 16:02:06 +00:00
{
2008-08-02 22:53:21 +00:00
if ( ! HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) {
/* It is the first time the problem occured, set the "train stuck" flag. */
SetBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
2009-01-09 22:48:57 +00:00
v - > load_unload_time_rem = 0 ;
2008-08-02 22:53:21 +00:00
/* Stop train */
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
SetLastSpeed ( v , 0 ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
}
}
static void SwapTrainFlags ( uint16 * swap_flag1 , uint16 * swap_flag2 )
{
uint16 flag1 = * swap_flag1 ;
uint16 flag2 = * swap_flag2 ;
2005-01-09 16:02:06 +00:00
/* Clear the flags */
2007-11-19 21:32:20 +00:00
ClrBit ( * swap_flag1 , VRF_GOINGUP ) ;
ClrBit ( * swap_flag1 , VRF_GOINGDOWN ) ;
ClrBit ( * swap_flag2 , VRF_GOINGUP ) ;
ClrBit ( * swap_flag2 , VRF_GOINGDOWN ) ;
2005-01-09 16:02:06 +00:00
/* Reverse the rail-flags (if needed) */
2007-11-19 21:02:30 +00:00
if ( HasBit ( flag1 , VRF_GOINGUP ) ) {
2007-11-20 13:35:54 +00:00
SetBit ( * swap_flag2 , VRF_GOINGDOWN ) ;
2007-11-19 21:02:30 +00:00
} else if ( HasBit ( flag1 , VRF_GOINGDOWN ) ) {
2007-11-20 13:35:54 +00:00
SetBit ( * swap_flag2 , VRF_GOINGUP ) ;
2005-01-09 16:02:06 +00:00
}
2007-11-19 21:02:30 +00:00
if ( HasBit ( flag2 , VRF_GOINGUP ) ) {
2007-11-20 13:35:54 +00:00
SetBit ( * swap_flag1 , VRF_GOINGDOWN ) ;
2007-11-19 21:02:30 +00:00
} else if ( HasBit ( flag2 , VRF_GOINGDOWN ) ) {
2007-11-20 13:35:54 +00:00
SetBit ( * swap_flag1 , VRF_GOINGUP ) ;
2005-01-09 16:02:06 +00:00
}
}
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
}
2008-01-17 17:57:39 +00:00
/**
* Check if the vehicle is a train
* @ param v vehicle on tile
* @ return v if it is a train , NULL otherwise
*/
2008-08-01 15:07:31 +00:00
static Vehicle * TrainOnTileEnum ( Vehicle * v , void * )
2004-12-21 16:17:27 +00:00
{
2008-01-17 17:57:39 +00:00
return ( v - > type = = VEH_TRAIN ) ? v : NULL ;
}
/**
* Checks if a train is approaching a rail - road crossing
* @ param v vehicle on tile
* @ param data tile with crossing we are testing
* @ return v if it is approaching a crossing , NULL otherwise
*/
2008-08-01 15:07:31 +00:00
static Vehicle * TrainApproachingCrossingEnum ( Vehicle * v , void * data )
2008-01-17 17:57:39 +00:00
{
/* not a train || not front engine || crashed */
if ( v - > type ! = VEH_TRAIN | | ! IsFrontEngine ( v ) | | v - > vehstatus & VS_CRASHED ) return NULL ;
TileIndex tile = * ( TileIndex * ) data ;
if ( TrainApproachingCrossingTile ( v ) ! = tile ) return NULL ;
2004-12-21 16:17:27 +00:00
return v ;
}
2008-01-17 17:57:39 +00:00
/**
* Finds a vehicle approaching rail - road crossing
* @ param tile tile to test
2008-09-07 11:23:10 +00:00
* @ return true if a vehicle is approaching the crossing
2008-01-17 17:57:39 +00:00
* @ pre tile is a rail - road crossing
*/
2008-09-07 11:23:10 +00:00
static bool TrainApproachingCrossing ( TileIndex tile )
2008-01-17 17:57:39 +00:00
{
assert ( IsLevelCrossingTile ( tile ) ) ;
2008-02-18 18:35:36 +00:00
DiagDirection dir = AxisToDiagDir ( GetCrossingRailAxis ( tile ) ) ;
2008-01-17 17:57:39 +00:00
TileIndex tile_from = tile + TileOffsByDiagDir ( dir ) ;
2008-09-07 11:23:10 +00:00
if ( HasVehicleOnPos ( tile_from , & tile , & TrainApproachingCrossingEnum ) ) return true ;
2008-01-17 17:57:39 +00:00
dir = ReverseDiagDir ( dir ) ;
tile_from = tile + TileOffsByDiagDir ( dir ) ;
2008-09-07 11:23:10 +00:00
return HasVehicleOnPos ( tile_from , & tile , & TrainApproachingCrossingEnum ) ;
2008-01-17 17:57:39 +00:00
}
/**
* Sets correct crossing state
* @ param tile tile to update
2008-01-17 20:41:33 +00:00
* @ param sound should we play sound ?
2008-01-17 17:57:39 +00:00
* @ pre tile is a rail - road crossing
*/
2008-01-17 20:41:33 +00:00
void UpdateLevelCrossing ( TileIndex tile , bool sound )
2005-01-23 10:40:54 +00:00
{
2008-01-17 17:57:39 +00:00
assert ( IsLevelCrossingTile ( tile ) ) ;
2008-08-02 22:57:18 +00:00
/* train on crossing || train approaching crossing || reserved */
2008-09-07 11:23:10 +00:00
bool new_state = HasVehicleOnPos ( tile , NULL , & TrainOnTileEnum ) | | TrainApproachingCrossing ( tile ) | | GetCrossingReservation ( tile ) ;
2008-01-17 17:57:39 +00:00
2008-01-17 20:41:33 +00:00
if ( new_state ! = IsCrossingBarred ( tile ) ) {
if ( new_state & & sound ) {
SndPlayTileFx ( SND_0E_LEVEL_CROSSING , tile ) ;
}
SetCrossingBarred ( tile , new_state ) ;
MarkTileDirtyByTile ( tile ) ;
}
2005-01-23 10:40:54 +00:00
}
2008-01-17 17:57:39 +00:00
2008-01-18 21:44:20 +00:00
/**
* Bars crossing and plays ding - ding sound if not barred already
* @ param tile tile with crossing
* @ pre tile is a rail - road crossing
*/
static inline void MaybeBarCrossingWithSound ( TileIndex tile )
{
if ( ! IsCrossingBarred ( tile ) ) {
BarCrossing ( tile ) ;
SndPlayTileFx ( SND_0E_LEVEL_CROSSING , tile ) ;
MarkTileDirtyByTile ( tile ) ;
}
}
2005-06-06 22:44:11 +00:00
/**
* Advances wagons for train reversing , needed for variable length wagons .
2008-03-19 20:50:19 +00:00
* This one is called before the train is reversed .
2005-06-06 22:44:11 +00:00
* @ param v First vehicle in chain
*/
2008-03-19 20:50:19 +00:00
static void AdvanceWagonsBeforeSwap ( Vehicle * v )
2005-06-06 22:44:11 +00:00
{
2007-02-25 09:27:03 +00:00
Vehicle * base = v ;
2008-03-19 20:50:19 +00:00
Vehicle * first = base ; // first vehicle to move
Vehicle * last = GetLastVehicleInChain ( v ) ; // last vehicle to move
2007-02-25 09:27:03 +00:00
uint length = CountVehiclesInChain ( v ) ;
2005-06-06 22:44:11 +00:00
while ( length > 2 ) {
2008-03-19 20:50:19 +00:00
last = last - > Previous ( ) ;
first = first - > Next ( ) ;
2005-06-06 22:44:11 +00:00
2008-03-19 20:50:19 +00:00
int differential = base - > u . rail . cached_veh_length - last - > u . rail . cached_veh_length ;
2005-06-06 22:44:11 +00:00
2008-03-19 20:42:05 +00:00
/* do not update images now
2008-03-19 20:50:19 +00:00
* negative differential will be handled in AdvanceWagonsAfterSwap ( ) */
2008-03-19 20:42:05 +00:00
for ( int i = 0 ; i < differential ; i + + ) TrainController ( first , last - > Next ( ) , false ) ;
2005-06-06 22:44:11 +00:00
2008-03-19 20:50:19 +00:00
base = first ; // == base->Next()
length - = 2 ;
}
}
/**
* Advances wagons for train reversing , needed for variable length wagons .
* This one is called after the train is reversed .
* @ param v First vehicle in chain
*/
static void AdvanceWagonsAfterSwap ( Vehicle * v )
{
/* first of all, fix the situation when the train was entering a depot */
Vehicle * dep = v ; // last vehicle in front of just left depot
while ( dep - > Next ( ) ! = NULL & & ( dep - > u . rail . track = = TRACK_BIT_DEPOT | | dep - > Next ( ) - > u . rail . track ! = TRACK_BIT_DEPOT ) ) {
dep = dep - > Next ( ) ; // find first vehicle outside of a depot, with next vehicle inside a depot
}
Vehicle * leave = dep - > Next ( ) ; // first vehicle in a depot we are leaving now
if ( leave ! = NULL ) {
/* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
int d = TicksToLeaveDepot ( dep ) ;
if ( d < = 0 ) {
leave - > vehstatus & = ~ VS_HIDDEN ; // move it out of the depot
2008-05-14 18:31:21 +00:00
leave - > u . rail . track = TrackToTrackBits ( GetRailDepotTrack ( leave - > tile ) ) ;
2008-03-19 20:50:19 +00:00
for ( int i = 0 ; i > = d ; i - - ) TrainController ( leave , NULL , false ) ; // maybe move it, and maybe let another wagon leave
}
} else {
dep = NULL ; // no vehicle in a depot, so no vehicle leaving a depot
}
Vehicle * base = v ;
Vehicle * first = base ; // first vehicle to move
Vehicle * last = GetLastVehicleInChain ( v ) ; // last vehicle to move
uint length = CountVehiclesInChain ( v ) ;
/* we have to make sure all wagons that leave a depot because of train reversing are moved coorectly
* they have already correct spacing , so we have to make sure they are moved how they should */
bool nomove = ( dep = = NULL ) ; // if there is no vehicle leaving a depot, limit the number of wagons moved immediatelly
while ( length > 2 ) {
/* we reached vehicle (originally) in front of a depot, stop now
* ( we would move wagons that are alredy moved with new wagon length ) */
if ( base = = dep ) break ;
/* the last wagon was that one leaving a depot, so do not move it anymore */
if ( last = = dep ) nomove = true ;
last = last - > Previous ( ) ;
2007-08-30 13:03:56 +00:00
first = first - > Next ( ) ;
2008-03-19 20:50:19 +00:00
int differential = last - > u . rail . cached_veh_length - base - > u . rail . cached_veh_length ;
/* do not update images now */
for ( int i = 0 ; i < differential ; i + + ) TrainController ( first , ( nomove ? last - > Next ( ) : NULL ) , false ) ;
base = first ; // == base->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 )
{
2008-04-17 18:24:45 +00:00
if ( IsRailDepotTile ( v - > tile ) ) {
2006-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 17:04:08 +00:00
2008-08-02 22:56:07 +00:00
/* Clear path reservation in front. */
FreeTrainTrackReservation ( v ) ;
2004-12-21 16:02:14 +00:00
/* Check if we were approaching a rail/road-crossing */
2008-01-17 17:57:39 +00:00
TileIndex crossing = TrainApproachingCrossingTile ( v ) ;
2004-12-21 16:02:14 +00:00
2007-04-04 03:21:14 +00:00
/* count number of vehicles */
2008-04-07 12:36:50 +00:00
int r = CountVehiclesInChain ( v ) - 1 ; // number of vehicles - 1
2004-08-09 17:04:08 +00:00
2008-03-19 20:50:19 +00:00
AdvanceWagonsBeforeSwap ( v ) ;
2005-06-06 22:44:11 +00:00
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 ) ;
2008-03-19 20:50:19 +00:00
AdvanceWagonsAfterSwap ( v ) ;
2005-06-06 22:44:11 +00:00
2008-04-17 18:24:45 +00:00
if ( IsRailDepotTile ( v - > tile ) ) {
2006-10-05 12:59:28 +00:00
InvalidateWindowData ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2004-08-09 17:04:08 +00:00
2008-04-14 20:48:17 +00:00
ToggleBit ( v - > u . rail . flags , VRF_TOGGLE_REVERSE ) ;
2007-12-27 13:25:23 +00:00
2007-11-19 21:32:20 +00:00
ClrBit ( v - > u . rail . flags , VRF_REVERSING ) ;
2008-01-17 17:57:39 +00:00
2008-03-03 21:42:37 +00:00
/* recalculate cached data */
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( v , true ) ;
2008-03-03 21:42:37 +00:00
/* update all images */
for ( Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) u - > cur_image = u - > GetImage ( u - > direction ) ;
2008-01-17 17:57:39 +00:00
/* update crossing we were approaching */
2008-01-17 19:49:06 +00:00
if ( crossing ! = INVALID_TILE ) UpdateLevelCrossing ( crossing ) ;
2008-01-17 17:57:39 +00:00
/* maybe we are approaching crossing now, after reversal */
crossing = TrainApproachingCrossingTile ( v ) ;
2008-01-18 21:44:20 +00:00
if ( crossing ! = INVALID_TILE ) MaybeBarCrossingWithSound ( crossing ) ;
2008-08-02 22:56:07 +00:00
/* If we are inside a depot after reversing, don't bother with path reserving. */
2008-08-22 22:28:26 +00:00
if ( v - > u . rail . track & TRACK_BIT_DEPOT ) {
/* Can't be stuck here as inside a depot is always a safe tile. */
if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
return ;
}
2008-08-02 22:56:07 +00:00
/* TrainExitDir does not always produce the desired dir for depots and
* tunnels / bridges that is needed for UpdateSignalsOnSegment . */
DiagDirection dir = TrainExitDir ( v - > direction , v - > u . rail . track ) ;
if ( IsRailDepotTile ( v - > tile ) | | IsTileType ( v - > tile , MP_TUNNELBRIDGE ) ) dir = INVALID_DIAGDIR ;
if ( UpdateSignalsOnSegment ( v - > tile , dir , v - > owner ) = = SIGSEG_PBS | | _settings_game . pf . reserve_paths ) {
/* If we are currently on a tile with conventional signals, we can't treat the
* current tile as a safe tile or we would enter a PBS block without a reservation . */
bool first_tile_okay = ! ( IsTileType ( v - > tile , MP_RAILWAY ) & &
HasSignalOnTrackdir ( v - > tile , GetVehicleTrackdir ( v ) ) & &
! IsPbsSignal ( GetSignalType ( v - > tile , FindFirstTrack ( v - > u . rail . track ) ) ) ) ;
2008-08-08 13:29:18 +00:00
if ( IsRailwayStationTile ( v - > tile ) ) SetRailwayStationPlatformReservation ( v - > tile , TrackdirToExitdir ( GetVehicleTrackdir ( v ) ) , true ) ;
2008-08-10 21:56:47 +00:00
if ( TryPathReserve ( v , false , first_tile_okay ) ) {
2008-08-02 22:56:07 +00:00
/* Do a look-ahead now in case our current tile was already a safe tile. */
CheckNextTrainTile ( v ) ;
2008-08-10 21:56:47 +00:00
} else if ( v - > current_order . GetType ( ) ! = OT_LOADING ) {
/* Do not wait for a way out when we're still loading */
MarkTrainAsStuck ( v ) ;
2008-08-02 22:56:07 +00:00
}
2009-01-05 20:29:00 +00:00
} else if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) {
/* A train not inside a PBS block can't be stuck. */
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
v - > load_unload_time_rem = 0 ;
2008-08-02 22:56:07 +00:00
}
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
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdReverseTrainDirection ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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
2008-04-07 12:36:50 +00:00
if ( p2 ! = 0 ) {
2007-04-04 03:21:14 +00:00
/* turn a single unit around */
2006-03-18 13:00:32 +00:00
2007-11-19 21:02:30 +00:00
if ( IsMultiheaded ( v ) | | HasBit ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_VEHICLE_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 ) {
2007-11-20 14:11:19 +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 ) {
2008-05-29 15:13:28 +00:00
if ( _settings_game . vehicle . realistic_acceleration & & v - > cur_speed ! = 0 ) {
2007-11-20 14:11:19 +00:00
ToggleBit ( v - > u . rail . flags , VRF_REVERSING ) ;
2006-03-18 13:00:32 +00:00
} else {
v - > cur_speed = 0 ;
SetLastSpeed ( v , 0 ) ;
2008-09-21 18:28:35 +00:00
HideFillingPercent ( & v - > fill_percent_te_id ) ;
2006-03-18 13:00:32 +00:00
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
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdForceTrainProceed ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdRefitRailVehicle ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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-11-19 21:02:30 +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 ) ;
2007-12-27 14:10:47 +00:00
if ( v - > vehstatus & VS_CRASHED ) return_cmd_error ( STR_CAN_T_REFIT_DESTROYED_VEHICLE ) ;
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
2008-01-09 16:55:48 +00:00
CommandCost cost ( EXPENSES_TRAIN_RUN ) ;
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 ;
2007-11-19 21:02:30 +00:00
if ( HasBit ( EngInfo ( v - > engine_type ) - > callbackmask , CBM_VEHICLE_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
2008-11-13 20:26:06 +00:00
if ( new_cid ! = v - > cargo_type ) {
cost . AddCost ( GetRefitCost ( v - > engine_type ) ) ;
}
2006-08-09 20:44:23 +00:00
2008-11-13 20:26:06 +00:00
num + = amount ;
if ( flags & DC_EXEC ) {
v - > cargo . Truncate ( ( v - > cargo_type = = new_cid ) ? amount : 0 ) ;
v - > cargo_type = new_cid ;
v - > cargo_cap = amount ;
v - > cargo_subtype = new_subtype ;
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
InvalidateWindowClassesData ( WC_TRAINS_LIST , 0 ) ;
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 */
2008-07-24 15:19:26 +00:00
if ( flags & DC_EXEC ) TrainConsistChanged ( GetVehicle ( p1 ) - > First ( ) , false ) ;
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 ;
2008-09-30 20:39:50 +00:00
Owner 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 ;
2005-05-07 22:00:36 +00:00
tfdd . reverse = false ;
2004-08-09 17:04:08 +00:00
2008-08-17 11:04:37 +00:00
if ( IsRailDepotTile ( v - > tile ) ) {
tfdd . tile = v - > tile ;
tfdd . best_length = 0 ;
return tfdd ;
}
2008-08-02 22:53:51 +00:00
PBSTileInfo origin = FollowTrainReservation ( v ) ;
if ( IsRailDepotTile ( origin . tile ) ) {
tfdd . tile = origin . tile ;
2004-09-23 21:20:38 +00:00
tfdd . best_length = 0 ;
return tfdd ;
}
2008-08-17 11:04:37 +00:00
tfdd . best_length = UINT_MAX ;
2008-08-02 22:53:51 +00:00
uint8 pathfinder = _settings_game . pf . pathfinder_for_trains ;
if ( ( _settings_game . pf . reserve_paths | | HasReservedTracks ( v - > tile , v - > u . rail . track ) ) & & pathfinder = = VPF_NTP ) pathfinder = VPF_NPF ;
switch ( pathfinder ) {
2008-02-13 17:54:11 +00:00
case VPF_YAPF : { /* YAPF */
bool found = YapfFindNearestRailDepotTwoWay ( v , max_distance , NPF_INFINITE_PENALTY , & tfdd . tile , & tfdd . reverse ) ;
tfdd . best_length = found ? max_distance / 2 : UINT_MAX ; // some fake distance or NOT_FOUND
} break ;
case VPF_NPF : { /* NPF */
2008-04-07 12:36:50 +00:00
const Vehicle * last = GetLastVehicleInChain ( v ) ;
2008-02-13 17:54:11 +00:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
Trackdir trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
assert ( trackdir ! = INVALID_TRACKDIR ) ;
NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay ( v - > tile , trackdir , false , last - > tile , trackdir_rev , false , TRANSPORT_RAIL , 0 , v - > owner , v - > u . rail . compatible_railtypes , NPF_INFINITE_PENALTY ) ;
if ( ftd . best_bird_dist = = 0 ) {
/* Found target */
tfdd . tile = ftd . node . tile ;
/* Our caller expects a number of tiles, so we just approximate that
2008-04-07 12:36:50 +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 . */
2008-02-13 17:54:11 +00:00
tfdd . best_length = ftd . best_path_dist / NPF_TILE_LENGTH ;
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) ) tfdd . reverse = true ;
}
} break ;
default :
case VPF_NTP : { /* NTP */
/* search in the forward direction first. */
DiagDirection i = TrainExitDir ( v - > direction , v - > u . rail . track ) ;
2008-08-02 22:53:51 +00:00
NewTrainPathfind ( v - > tile , 0 , v - > u . rail . compatible_railtypes , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2008-02-13 17:54:11 +00:00
if ( tfdd . best_length = = UINT_MAX ) {
tfdd . reverse = true ;
/* search in backwards direction */
i = TrainExitDir ( ReverseDir ( v - > direction ) , v - > u . rail . track ) ;
2008-08-02 22:53:51 +00:00
NewTrainPathfind ( v - > tile , 0 , v - > u . rail . compatible_railtypes , i , ( NTPEnumProc * ) NtpCallbFindDepot , & tfdd ) ;
2008-02-13 17:54:11 +00:00
}
} break ;
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
}
2008-04-11 08:14:43 +00:00
bool Train : : FindClosestDepot ( TileIndex * location , DestinationID * destination , bool * reverse )
{
TrainFindDepotData tfdd = FindClosestTrainDepot ( this , 0 ) ;
2008-10-14 18:38:51 +00:00
if ( tfdd . best_length = = UINT_MAX ) return false ;
2008-04-11 08:14:43 +00:00
if ( location ! = NULL ) * location = tfdd . tile ;
if ( destination ! = NULL ) * destination = GetDepotByTile ( tfdd . tile ) - > index ;
if ( reverse ! = NULL ) * reverse = tfdd . reverse ;
return true ;
}
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
*/
2008-12-28 14:37:19 +00:00
CommandCost CmdSendTrainToDepot ( TileIndex tile , uint32 flags , uint32 p1 , uint32 p2 , const char * text )
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 ;
2008-09-30 20:39:50 +00:00
return SendAllVehiclesToDepot ( VEH_TRAIN , flags , p2 & DEPOT_SERVICE , _current_company , ( 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
2008-04-11 08:40:10 +00:00
if ( v - > type ! = VEH_TRAIN ) return CMD_ERROR ;
2007-04-20 11:39:01 +00:00
2008-04-11 08:40:10 +00:00
return v - > SendToDepot ( flags , ( DepotCommand ) ( p2 & DEPOT_COMMAND_MASK ) ) ;
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
} ;
2008-04-07 12:36:50 +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
2008-04-07 12:36:50 +00:00
if ( v - > vehstatus & VS_TRAIN_SLOWING | | v - > load_unload_time_rem ! = 0 | | v - > cur_speed < 2 ) {
2004-08-09 17:04:08 +00:00
return ;
2008-04-07 12:36:50 +00:00
}
2004-08-09 17:04:08 +00:00
2008-04-07 12:36:50 +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 ) ;
2007-11-19 21:02:30 +00:00
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 */
2008-04-17 18:24:45 +00:00
if ( IsRailDepotTile ( v - > tile ) | | IsTunnelTile ( v - > tile ) ) continue ;
2006-04-19 16:10:17 +00:00
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
2007-11-19 21:02:30 +00:00
if ( HasBit ( v - > u . rail . flags , VRF_REVERSE_DIRECTION ) ) {
2006-03-18 13:00:32 +00:00
x = - x ;
y = - y ;
}
2005-11-04 12:58:18 +00:00
switch ( effect_type ) {
2008-04-07 12:36:50 +00:00
case 0 :
/* steam smoke. */
if ( GB ( v - > tick_counter , 0 , 4 ) = = 0 ) {
CreateEffectVehicleRel ( v , x , y , 10 , EV_STEAM_SMOKE ) ;
sound = true ;
}
break ;
2004-08-09 17:04:08 +00:00
2008-04-07 12:36:50 +00:00
case 1 :
/* diesel smoke */
if ( u - > cur_speed < = 40 & & Chance16 ( 15 , 128 ) ) {
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_DIESEL_SMOKE ) ;
sound = true ;
}
break ;
2004-08-09 17:04:08 +00:00
2008-04-07 12:36:50 +00:00
case 2 :
/* blue spark */
if ( GB ( v - > tick_counter , 0 , 2 ) = = 0 & & Chance16 ( 1 , 45 ) ) {
CreateEffectVehicleRel ( v , 0 , 0 , 10 , EV_ELECTRIC_SPARK ) ;
sound = true ;
}
break ;
default :
break ;
2004-08-09 17:04:08 +00:00
}
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
}
2008-02-13 09:34:56 +00:00
void Train : : PlayLeaveStationSound ( ) const
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
} ;
2008-02-13 09:34:56 +00:00
if ( PlayVehicleSound ( this , VSE_START ) ) return ;
2004-08-09 17:04:08 +00:00
2008-02-13 09:34:56 +00:00
EngineID engtype = this - > engine_type ;
SndPlayVehicleFx ( sfx [ RailVehInfo ( engtype ) - > engclass ] , this ) ;
2007-05-07 15:58:05 +00:00
}
2008-08-02 22:55:23 +00:00
/** Check if the train is on the last reserved tile and try to extend the path then. */
static void CheckNextTrainTile ( Vehicle * v )
{
/* Don't do any look-ahead if path_backoff_interval is 255. */
if ( _settings_game . pf . path_backoff_interval = = 255 ) return ;
2008-09-09 18:47:53 +00:00
/* Exit if we reached our destination depot or are inside a depot. */
if ( ( v - > tile = = v - > dest_tile & & v - > current_order . IsType ( OT_GOTO_DEPOT ) ) | | v - > u . rail . track & TRACK_BIT_DEPOT ) return ;
2008-08-02 22:55:23 +00:00
/* Exit if we are on a station tile and are going to stop. */
if ( IsRailwayStationTile ( v - > tile ) & & v - > current_order . ShouldStopAtStation ( v , GetStationIndex ( v - > tile ) ) ) return ;
/* Exit if the current order doesn't have a destination, but the train has orders. */
2009-01-12 14:56:31 +00:00
if ( ( v - > current_order . IsType ( OT_NOTHING ) | | v - > current_order . IsType ( OT_LEAVESTATION ) | | v - > current_order . IsType ( OT_LOADING ) ) & & v - > GetNumOrders ( ) > 0 ) return ;
2008-08-02 22:55:23 +00:00
Trackdir td = GetVehicleTrackdir ( v ) ;
/* On a tile with a red non-pbs signal, don't look ahead. */
if ( IsTileType ( v - > tile , MP_RAILWAY ) & & HasSignalOnTrackdir ( v - > tile , td ) & &
! IsPbsSignal ( GetSignalType ( v - > tile , TrackdirToTrack ( td ) ) ) & &
GetSignalStateByTrackdir ( v - > tile , td ) = = SIGNAL_STATE_RED ) return ;
CFollowTrackRail ft ( v ) ;
if ( ! ft . Follow ( v - > tile , td ) ) return ;
if ( ! HasReservedTracks ( ft . m_new_tile , TrackdirBitsToTrackBits ( ft . m_new_td_bits ) ) ) {
/* Next tile is not reserved. */
if ( KillFirstBit ( ft . m_new_td_bits ) = = TRACKDIR_BIT_NONE ) {
if ( HasPbsSignalOnTrackdir ( ft . m_new_tile , FindFirstTrackdir ( ft . m_new_td_bits ) ) ) {
/* If the next tile is a PBS signal, try to make a reservation. */
TrackBits tracks = TrackdirBitsToTrackBits ( ft . m_new_td_bits ) ;
if ( _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . forbid_90_deg ) {
tracks & = ~ TrackCrossesTracks ( TrackdirToTrack ( ft . m_old_td ) ) ;
}
ChooseTrainTrack ( v , ft . m_new_tile , ft . m_exitdir , tracks , false , NULL , false ) ;
}
}
}
}
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 ;
}
2008-08-02 22:55:52 +00:00
SigSegState seg_state ;
2004-08-09 17:04:08 +00:00
if ( v - > u . rail . force_proceed = = 0 ) {
2008-08-02 22:55:52 +00:00
/* force proceed was not pressed */
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 ;
2008-08-02 22:55:52 +00:00
seg_state = _settings_game . pf . reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment ( v - > tile , INVALID_DIAGDIR , v - > owner ) ;
if ( seg_state = = SIGSEG_FULL | | GetDepotWaypointReservation ( v - > tile ) ) {
/* Full and no PBS signal in block or depot reserved, can't exit. */
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
}
2008-08-02 22:55:52 +00:00
} else {
seg_state = _settings_game . pf . reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment ( v - > tile , INVALID_DIAGDIR , v - > owner ) ;
}
/* Only leave when we can reserve a path to our destination. */
if ( seg_state = = SIGSEG_PBS & & ! TryPathReserve ( v ) & & v - > u . rail . force_proceed = = 0 ) {
/* No path and no force proceed. */
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
MarkTrainAsStuck ( v ) ;
return true ;
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
2008-08-02 22:55:52 +00:00
SetDepotWaypointReservation ( v - > tile , true ) ;
if ( _settings_client . gui . show_track_reservation ) MarkTileDirtyByTile ( v - > tile ) ;
2004-12-09 21:46:56 +00:00
VehicleServiceInDepot ( v ) ;
2005-01-24 22:24:47 +00:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2008-02-13 09:34:56 +00:00
v - > PlayLeaveStationSound ( ) ;
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 ) ;
2008-01-15 15:00:01 +00:00
UpdateSignalsOnSegment ( v - > tile , INVALID_DIAGDIR , v - > owner ) ;
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 ;
}
2008-08-02 22:53:37 +00:00
/** Clear the reservation of a tile that was just left by a wagon on track_dir. */
2008-09-09 19:02:47 +00:00
static void ClearPathReservation ( const Vehicle * v , TileIndex tile , Trackdir track_dir )
2008-08-02 22:53:37 +00:00
{
DiagDirection dir = TrackdirToExitdir ( track_dir ) ;
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) ) {
/* Are we just leaving a tunnel/bridge? */
if ( GetTunnelBridgeDirection ( tile ) = = ReverseDiagDir ( dir ) ) {
TileIndex end = GetOtherTunnelBridgeEnd ( tile ) ;
2008-09-09 19:02:47 +00:00
if ( ! HasVehicleOnTunnelBridge ( tile , end , v ) ) {
/* Free the reservation only if no other train is on the tiles. */
SetTunnelBridgeReservation ( tile , false ) ;
SetTunnelBridgeReservation ( end , false ) ;
2008-08-02 22:53:37 +00:00
2008-09-09 19:02:47 +00:00
if ( _settings_client . gui . show_track_reservation ) {
MarkTileDirtyByTile ( tile ) ;
MarkTileDirtyByTile ( end ) ;
}
2008-08-02 22:53:37 +00:00
}
}
} else if ( IsRailwayStationTile ( tile ) ) {
TileIndex new_tile = TileAddByDiagDir ( tile , dir ) ;
/* If the new tile is not a further tile of the same station, we
* clear the reservation for the whole platform . */
if ( ! IsCompatibleTrainStationTile ( new_tile , tile ) ) {
SetRailwayStationPlatformReservation ( tile , ReverseDiagDir ( dir ) , false ) ;
}
} else {
/* Any other tile */
UnreserveRailTrack ( tile , TrackdirToTrack ( track_dir ) ) ;
}
}
2008-08-02 22:53:51 +00:00
/** Free the reserved path in front of a vehicle. */
void FreeTrainTrackReservation ( const Vehicle * v , TileIndex origin , Trackdir orig_td )
{
assert ( IsFrontEngine ( v ) ) ;
TileIndex tile = origin ! = INVALID_TILE ? origin : v - > tile ;
Trackdir td = orig_td ! = INVALID_TRACKDIR ? orig_td : GetVehicleTrackdir ( v ) ;
bool free_tile = tile ! = v - > tile | | ! ( IsRailwayStationTile ( v - > tile ) | | IsTileType ( v - > tile , MP_TUNNELBRIDGE ) ) ;
2008-08-22 22:42:21 +00:00
StationID station_id = IsRailwayStationTile ( v - > tile ) ? GetStationIndex ( v - > tile ) : INVALID_STATION ;
2008-08-02 22:53:51 +00:00
/* Don't free reservation if it's not ours. */
if ( TracksOverlap ( GetReservedTrackbits ( tile ) | TrackToTrackBits ( TrackdirToTrack ( td ) ) ) ) return ;
CFollowTrackRail ft ( v , GetRailTypeInfo ( v - > u . rail . railtype ) - > compatible_railtypes ) ;
while ( ft . Follow ( tile , td ) ) {
tile = ft . m_new_tile ;
TrackdirBits bits = ( TrackdirBits ) ( ft . m_new_td_bits & ( GetReservedTrackbits ( tile ) * 0x101 ) ) ;
td = RemoveFirstTrackdir ( & bits ) ;
assert ( bits = = TRACKDIR_BIT_NONE ) ;
if ( ! IsValidTrackdir ( td ) ) break ;
if ( IsTileType ( tile , MP_RAILWAY ) ) {
if ( HasSignalOnTrackdir ( tile , td ) & & ! IsPbsSignal ( GetSignalType ( tile , TrackdirToTrack ( td ) ) ) ) {
/* Conventional signal along trackdir: remove reservation and stop. */
UnreserveRailTrack ( tile , TrackdirToTrack ( td ) ) ;
break ;
}
if ( HasPbsSignalOnTrackdir ( tile , td ) ) {
if ( GetSignalStateByTrackdir ( tile , td ) = = SIGNAL_STATE_RED ) {
/* Red PBS signal? Can't be our reservation, would be green then. */
break ;
} else {
/* Turn the signal back to red. */
SetSignalStateByTrackdir ( tile , td , SIGNAL_STATE_RED ) ;
MarkTileDirtyByTile ( tile ) ;
}
} else if ( HasSignalOnTrackdir ( tile , ReverseTrackdir ( td ) ) & & IsOnewaySignal ( tile , TrackdirToTrack ( td ) ) ) {
break ;
}
}
/* Don't free first station/bridge/tunnel if we are on it. */
2008-09-09 19:02:47 +00:00
if ( free_tile | | ( ! ( ft . m_is_station & & GetStationIndex ( ft . m_new_tile ) = = station_id ) & & ! ft . m_is_tunnel & & ! ft . m_is_bridge ) ) ClearPathReservation ( v , tile , td ) ;
2008-08-02 22:53:51 +00:00
free_tile = true ;
}
}
2008-04-07 12:36:50 +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 ;
2008-04-07 12:36:50 +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 ;
}
}
2008-04-07 12:36:50 +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 ;
2008-04-06 07:48:51 +00:00
fd - > station_index = v - > current_order . IsType ( OT_GOTO_STATION ) ? v - > current_order . GetDestination ( ) : 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 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
2008-08-02 22:53:51 +00:00
/**
* Perform pathfinding for a train .
*
* @ param v The train
* @ param tile The tile the train is about to enter
* @ param enterdir Diagonal direction the train is coming from
* @ param tracks Usable tracks on the new tile
* @ param path_not_found [ out ] Set to false if the pathfinder couldn ' t find a way to the destination
* @ param do_track_reservation
* @ param dest [ out ]
* @ return The best track the train should follow
*/
2009-01-10 00:31:47 +00:00
static Track DoTrainPathfind ( Vehicle * v , TileIndex tile , DiagDirection enterdir , TrackBits tracks , bool * path_not_found , bool do_track_reservation , PBSTileInfo * dest )
2004-09-10 19:02:27 +00:00
{
2007-01-10 18:56:51 +00:00
Track best_track ;
2006-10-17 16:16:19 +00:00
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
2008-08-02 22:53:51 +00:00
if ( path_not_found ) * path_not_found = false ;
2004-08-09 17:04:08 +00:00
2008-08-02 22:53:51 +00:00
uint8 pathfinder = _settings_game . pf . pathfinder_for_trains ;
if ( do_track_reservation & & pathfinder = = VPF_NTP ) pathfinder = VPF_NPF ;
2004-08-09 17:04:08 +00:00
2008-08-02 22:53:51 +00:00
switch ( pathfinder ) {
2008-02-13 17:54:11 +00:00
case VPF_YAPF : { /* YAPF */
2008-08-02 22:53:51 +00:00
Trackdir trackdir = YapfChooseRailTrack ( v , tile , enterdir , tracks , path_not_found , do_track_reservation , dest ) ;
2008-02-13 17:54:11 +00:00
if ( trackdir ! = INVALID_TRACKDIR ) {
best_track = TrackdirToTrack ( trackdir ) ;
} else {
best_track = FindFirstTrack ( tracks ) ;
}
} break ;
2006-05-27 16:12:16 +00:00
2008-02-13 17:54:11 +00:00
case VPF_NPF : { /* NPF */
void * perf = NpfBeginInterval ( ) ;
2005-01-31 11:23:10 +00:00
2008-02-13 17:54:11 +00:00
NPFFindStationOrTileData fstd ;
2008-08-02 22:53:51 +00:00
NPFFillWithOrderData ( & fstd , v , do_track_reservation ) ;
PBSTileInfo origin = FollowTrainReservation ( v ) ;
assert ( IsValidTrackdir ( origin . trackdir ) ) ;
2005-04-15 13:48:08 +00:00
2008-08-02 22:53:51 +00:00
NPFFoundTargetData ftd = NPFRouteToStationOrTile ( origin . tile , origin . trackdir , true , & fstd , TRANSPORT_RAIL , 0 , v - > owner , v - > u . rail . compatible_railtypes ) ;
if ( dest ! = NULL ) {
dest - > tile = ftd . node . tile ;
dest - > trackdir = ( Trackdir ) ftd . node . direction ;
dest - > okay = ftd . res_okay ;
}
2006-05-27 16:12:16 +00:00
2008-02-13 17:54:11 +00:00
if ( ftd . best_trackdir = = INVALID_TRACKDIR ) {
/* We are already at our target. Just do something
* @ todo maybe display error ?
* @ todo : go straight ahead if possible ? */
best_track = FindFirstTrack ( tracks ) ;
} else {
/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
* the direction we need to take to get there , if ftd . best_bird_dist is not 0 ,
* we did not find our target , but ftd . best_trackdir contains the direction leading
* to the tile closest to our target . */
2008-08-02 22:53:51 +00:00
if ( ftd . best_bird_dist ! = 0 & & path_not_found ! = NULL ) * path_not_found = true ;
2008-02-13 17:54:11 +00:00
/* Discard enterdir information, making it a normal track */
best_track = TrackdirToTrack ( ftd . best_trackdir ) ;
}
2006-05-27 16:12:16 +00:00
2008-02-13 17:54:11 +00:00
int time = NpfEndInterval ( perf ) ;
DEBUG ( yapf , 4 , " [NPFT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , _aystar_stats_open_size , _aystar_stats_closed_size ) ;
} break ;
2004-08-09 17:04:08 +00:00
2008-02-13 17:54:11 +00:00
default :
case VPF_NTP : { /* NTP */
void * perf = NpfBeginInterval ( ) ;
2004-09-10 19:02:27 +00:00
2008-02-13 17:54:11 +00:00
TrainTrackFollowerData fd ;
FillWithStationData ( & fd , v ) ;
2005-01-31 11:23:10 +00:00
2008-02-13 17:54:11 +00:00
/* New train pathfinding */
fd . best_bird_dist = UINT_MAX ;
fd . best_track_dist = UINT_MAX ;
fd . best_track = INVALID_TRACKDIR ;
2006-10-17 16:16:19 +00:00
2008-02-13 17:54:11 +00:00
NewTrainPathfind ( tile - TileOffsByDiagDir ( enterdir ) , v - > dest_tile ,
v - > u . rail . compatible_railtypes , enterdir , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
/* check whether the path was found or only 'guessed' */
2008-08-02 22:53:51 +00:00
if ( fd . best_bird_dist ! = 0 & & path_not_found ! = NULL ) * path_not_found = true ;
2008-02-13 17:54:11 +00:00
if ( fd . best_track = = INVALID_TRACKDIR ) {
/* blaha */
best_track = FindFirstTrack ( tracks ) ;
} else {
best_track = TrackdirToTrack ( fd . best_track ) ;
}
2006-05-27 16:12:16 +00:00
2008-02-13 17:54:11 +00:00
int time = NpfEndInterval ( perf ) ;
DEBUG ( yapf , 4 , " [NTPT] %d us - %d rounds - %d open - %d closed -- " , time , 0 , 0 , 0 ) ;
} break ;
2004-08-09 17:04:08 +00:00
}
2008-02-13 17:54:11 +00:00
2008-08-02 22:53:51 +00:00
# ifdef PF_BENCHMARK
TOC ( " PF time = " , 1 )
# endif
return best_track ;
}
2008-08-02 22:55:38 +00:00
/**
* Extend a train path as far as possible . Stops on encountering a safe tile ,
* another reservation or a track choice .
* @ return INVALID_TILE indicates that the reservation failed .
*/
static PBSTileInfo ExtendTrainReservation ( const Vehicle * v , TrackBits * new_tracks , DiagDirection * enterdir )
{
bool no_90deg_turns = _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . forbid_90_deg ;
PBSTileInfo origin = FollowTrainReservation ( v ) ;
CFollowTrackRail ft ( v ) ;
TileIndex tile = origin . tile ;
Trackdir cur_td = origin . trackdir ;
while ( ft . Follow ( tile , cur_td ) ) {
if ( KillFirstBit ( ft . m_new_td_bits ) = = TRACKDIR_BIT_NONE ) {
/* Possible signal tile. */
if ( HasOnewaySignalBlockingTrackdir ( ft . m_new_tile , FindFirstTrackdir ( ft . m_new_td_bits ) ) ) break ;
2009-01-12 17:11:45 +00:00
AI : : NewEvent ( v - > owner , new AIEventVehicleLost ( v - > index ) ) ;
2008-08-02 22:55:38 +00:00
}
if ( no_90deg_turns ) {
ft . m_new_td_bits & = ~ TrackdirCrossesTrackdirs ( ft . m_old_td ) ;
if ( ft . m_new_td_bits = = TRACKDIR_BIT_NONE ) break ;
}
/* Station, depot or waypoint are a possible target. */
bool target_seen = ft . m_is_station | | ( IsTileType ( ft . m_new_tile , MP_RAILWAY ) & & ! IsPlainRailTile ( ft . m_new_tile ) ) ;
if ( target_seen | | KillFirstBit ( ft . m_new_td_bits ) ! = TRACKDIR_BIT_NONE ) {
/* Choice found or possible target encountered.
* On finding a possible target , we need to stop and let the pathfinder handle the
* remaining path . This is because we don ' t know if this target is in one of our
* orders , so we might cause pathfinding to fail later on if we find a choice .
* This failure would cause a bogous call to TryReserveSafePath which might reserve
* a wrong path not leading to our next destination . */
if ( HasReservedTracks ( ft . m_new_tile , TrackdirBitsToTrackBits ( TrackdirReachesTrackdirs ( ft . m_old_td ) ) ) ) break ;
/* If we did skip some tiles, backtrack to the first skipped tile so the pathfinder
* actually starts its search at the first unreserved tile . */
if ( ft . m_tiles_skipped ! = 0 ) ft . m_new_tile - = TileOffsByDiagDir ( ft . m_exitdir ) * ft . m_tiles_skipped ;
/* Choice found, path valid but not okay. Save info about the choice tile as well. */
if ( new_tracks ) * new_tracks = TrackdirBitsToTrackBits ( ft . m_new_td_bits ) ;
if ( enterdir ) * enterdir = ft . m_exitdir ;
return PBSTileInfo ( ft . m_new_tile , ft . m_old_td , false ) ;
}
tile = ft . m_new_tile ;
cur_td = FindFirstTrackdir ( ft . m_new_td_bits ) ;
if ( IsSafeWaitingPosition ( v , tile , cur_td , true , no_90deg_turns ) ) {
bool wp_free = IsWaitingPositionFree ( v , tile , cur_td , no_90deg_turns ) ;
if ( ! ( wp_free & & TryReserveRailTrack ( tile , TrackdirToTrack ( cur_td ) ) ) ) break ;
/* Safe position is all good, path valid and okay. */
return PBSTileInfo ( tile , cur_td , true ) ;
}
if ( ! TryReserveRailTrack ( tile , TrackdirToTrack ( cur_td ) ) ) break ;
}
2008-09-24 23:25:24 +00:00
if ( ft . m_err = = CFollowTrackRail : : EC_OWNER | | ft . m_err = = CFollowTrackRail : : EC_NO_WAY ) {
2008-08-02 22:55:38 +00:00
/* End of line, path valid and okay. */
return PBSTileInfo ( ft . m_old_tile , ft . m_old_td , true ) ;
}
/* Sorry, can't reserve path, back out. */
tile = origin . tile ;
cur_td = origin . trackdir ;
TileIndex stopped = ft . m_old_tile ;
Trackdir stopped_td = ft . m_old_td ;
while ( tile ! = stopped | | cur_td ! = stopped_td ) {
if ( ! ft . Follow ( tile , cur_td ) ) break ;
if ( no_90deg_turns ) {
ft . m_new_td_bits & = ~ TrackdirCrossesTrackdirs ( ft . m_old_td ) ;
assert ( ft . m_new_td_bits ! = TRACKDIR_BIT_NONE ) ;
}
assert ( KillFirstBit ( ft . m_new_td_bits ) = = TRACKDIR_BIT_NONE ) ;
tile = ft . m_new_tile ;
cur_td = FindFirstTrackdir ( ft . m_new_td_bits ) ;
UnreserveRailTrack ( tile , TrackdirToTrack ( cur_td ) ) ;
}
/* Path invalid. */
return PBSTileInfo ( ) ;
}
2008-08-02 22:53:51 +00:00
/**
* Try to reserve any path to a safe tile , ignoring the vehicle ' s destination .
* Safe tiles are tiles in front of a signal , depots and station tiles at end of line .
*
* @ param v The vehicle .
* @ param tile The tile the search should start from .
* @ param td The trackdir the search should start from .
* @ param override_tailtype Whether all physically compatible railtypes should be followed .
* @ return True if a path to a safe stopping tile could be reserved .
*/
2009-01-10 00:31:47 +00:00
static bool TryReserveSafeTrack ( const Vehicle * v , TileIndex tile , Trackdir td , bool override_tailtype )
2008-08-02 22:53:51 +00:00
{
if ( _settings_game . pf . pathfinder_for_trains = = VPF_YAPF ) {
return YapfRailFindNearestSafeTile ( v , tile , td , override_tailtype ) ;
} else {
return NPFRouteToSafeTile ( v , tile , td , override_tailtype ) . res_okay ;
}
}
2008-10-18 13:13:27 +00:00
/** This class will save the current order of a vehicle and restore it on destruction. */
class VehicleOrderSaver
2008-08-02 22:54:23 +00:00
{
2008-10-18 13:13:27 +00:00
private :
Vehicle * v ;
Order old_order ;
TileIndex old_dest_tile ;
2008-11-19 14:48:12 +00:00
StationID old_last_station_visited ;
2008-10-18 13:13:27 +00:00
VehicleOrderID index ;
2008-08-02 22:54:23 +00:00
2008-10-18 13:13:27 +00:00
public :
2008-11-19 14:48:12 +00:00
VehicleOrderSaver ( Vehicle * _v ) :
v ( _v ) ,
old_order ( _v - > current_order ) ,
old_dest_tile ( _v - > dest_tile ) ,
old_last_station_visited ( _v - > last_station_visited ) ,
index ( _v - > cur_order_index )
2008-10-18 13:13:27 +00:00
{
}
~ VehicleOrderSaver ( )
{
this - > v - > current_order = this - > old_order ;
this - > v - > dest_tile = this - > old_dest_tile ;
2008-11-19 14:48:12 +00:00
this - > v - > last_station_visited = this - > old_last_station_visited ;
2008-10-18 13:13:27 +00:00
}
/**
* Set the current vehicle order to the next order in the order list .
2009-01-11 16:33:03 +00:00
* @ param skip_first Shall the first ( i . e . active ) order be skipped ?
2008-10-18 13:13:27 +00:00
* @ return True if a suitable next order could be found .
*/
2009-01-11 16:33:03 +00:00
bool SwitchToNextOrder ( bool skip_first )
2008-10-18 13:13:27 +00:00
{
2009-01-11 16:33:03 +00:00
if ( skip_first ) + + this - > index ;
2008-10-18 13:13:27 +00:00
2009-01-04 14:36:19 +00:00
int conditional_depth = 0 ;
2008-10-18 13:13:27 +00:00
do {
/* Wrap around. */
2009-01-03 13:20:32 +00:00
if ( this - > index > = this - > v - > GetNumOrders ( ) ) this - > index = 0 ;
2008-10-18 13:13:27 +00:00
Order * order = GetVehicleOrder ( this - > v , this - > index ) ;
assert ( order ! = NULL ) ;
switch ( order - > GetType ( ) ) {
case OT_GOTO_DEPOT :
/* Skip service in depot orders when the train doesn't need service. */
if ( ( order - > GetDepotOrderType ( ) & ODTFB_SERVICE ) & & ! this - > v - > NeedsServicing ( ) ) break ;
case OT_GOTO_STATION :
case OT_GOTO_WAYPOINT :
this - > v - > current_order = * order ;
UpdateOrderDest ( this - > v , order ) ;
return true ;
case OT_CONDITIONAL : {
2009-01-04 14:36:19 +00:00
if ( conditional_depth > this - > v - > GetNumOrders ( ) ) return false ;
2008-10-18 13:13:27 +00:00
VehicleOrderID next = ProcessConditionalOrder ( order , this - > v ) ;
if ( next ! = INVALID_VEH_ORDER_ID ) {
2009-01-04 14:36:19 +00:00
conditional_depth + + ;
2008-10-18 13:13:27 +00:00
this - > index = next ;
2008-10-19 17:16:26 +00:00
/* Don't increment next, so no break here. */
2008-10-18 13:13:27 +00:00
continue ;
}
break ;
2008-08-02 22:54:23 +00:00
}
2008-10-18 13:13:27 +00:00
default :
break ;
2008-08-02 22:54:23 +00:00
}
2008-10-19 17:16:26 +00:00
/* Don't increment inside the while because otherwise conditional
* orders can lead to an infinite loop . */
+ + this - > index ;
} while ( this - > index ! = this - > v - > cur_order_index ) ;
2008-08-02 22:54:23 +00:00
2008-10-18 13:13:27 +00:00
return false ;
}
} ;
2008-08-02 22:54:23 +00:00
2008-08-02 22:53:51 +00:00
/* choose a track */
2009-01-10 00:31:47 +00:00
static Track ChooseTrainTrack ( Vehicle * v , TileIndex tile , DiagDirection enterdir , TrackBits tracks , bool force_res , bool * got_reservation , bool mark_stuck )
2008-08-02 22:53:51 +00:00
{
Track best_track = INVALID_TRACK ;
bool do_track_reservation = _settings_game . pf . reserve_paths | | force_res ;
bool changed_signal = false ;
assert ( ( tracks & ~ TRACK_BIT_MASK ) = = 0 ) ;
if ( got_reservation ! = NULL ) * got_reservation = false ;
/* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
TrackBits res_tracks = ( TrackBits ) ( GetReservedTrackbits ( tile ) & DiagdirReachesTracks ( enterdir ) ) ;
/* Do we have a suitable reserved track? */
if ( res_tracks ! = TRACK_BIT_NONE ) return FindFirstTrack ( res_tracks ) ;
/* Quick return in case only one possible track is available */
if ( KillFirstBit ( tracks ) = = TRACK_BIT_NONE ) {
Track track = FindFirstTrack ( tracks ) ;
/* We need to check for signals only here, as a junction tile can't have signals. */
if ( track ! = INVALID_TRACK & & HasPbsSignalOnTrackdir ( tile , TrackEnterdirToTrackdir ( track , enterdir ) ) ) {
do_track_reservation = true ;
changed_signal = true ;
SetSignalStateByTrackdir ( tile , TrackEnterdirToTrackdir ( track , enterdir ) , SIGNAL_STATE_GREEN ) ;
} else if ( ! do_track_reservation ) {
return track ;
}
best_track = track ;
}
2008-08-02 22:55:38 +00:00
PBSTileInfo res_dest ( tile , INVALID_TRACKDIR , false ) ;
DiagDirection dest_enterdir = enterdir ;
2008-08-02 22:53:51 +00:00
if ( do_track_reservation ) {
2008-08-02 22:56:50 +00:00
/* Check if the train needs service here, so it has a chance to always find a depot.
* Also check if the current order is a service order so we don ' t reserve a path to
* the destination but instead to the next one if service isn ' t needed . */
CheckIfTrainNeedsService ( v ) ;
if ( v - > current_order . IsType ( OT_DUMMY ) | | v - > current_order . IsType ( OT_CONDITIONAL ) | | v - > current_order . IsType ( OT_GOTO_DEPOT ) ) ProcessOrders ( v ) ;
2008-08-02 22:55:38 +00:00
res_dest = ExtendTrainReservation ( v , & tracks , & dest_enterdir ) ;
if ( res_dest . tile = = INVALID_TILE ) {
/* Reservation failed? */
if ( mark_stuck ) MarkTrainAsStuck ( v ) ;
if ( changed_signal ) SetSignalStateByTrackdir ( tile , TrackEnterdirToTrackdir ( best_track , enterdir ) , SIGNAL_STATE_RED ) ;
return FindFirstTrack ( tracks ) ;
}
2008-08-02 22:53:51 +00:00
}
2008-10-18 13:13:31 +00:00
/* Save the current train order. The destructor will restore the old order on function exit. */
VehicleOrderSaver orders ( v ) ;
/* If the current tile is the destination of the current order and
2008-10-31 16:46:34 +00:00
* a reservation was requested , advance to the next order .
* Don ' t advance on a depot order as depots are always safe end points
* for a path and no look - ahead is necessary . This also avoids a
* problem with depot orders not part of the order list when the
* order list itself is empty . */
2009-01-11 16:33:03 +00:00
if ( v - > current_order . IsType ( OT_LEAVESTATION ) ) {
orders . SwitchToNextOrder ( false ) ;
} else if ( v - > current_order . IsType ( OT_LOADING ) | | ( ! v - > current_order . IsType ( OT_GOTO_DEPOT ) & & (
2008-12-20 00:16:23 +00:00
v - > current_order . IsType ( OT_GOTO_STATION ) ?
IsRailwayStationTile ( v - > tile ) & & v - > current_order . GetDestination ( ) = = GetStationIndex ( v - > tile ) :
2009-01-11 16:33:03 +00:00
v - > tile = = v - > dest_tile ) ) ) {
orders . SwitchToNextOrder ( true ) ;
2008-10-18 13:13:31 +00:00
}
2008-08-02 22:55:38 +00:00
if ( res_dest . tile ! = INVALID_TILE & & ! res_dest . okay ) {
/* Pathfinders are able to tell that route was only 'guessed'. */
bool path_not_found = false ;
TileIndex new_tile = res_dest . tile ;
Track next_track = DoTrainPathfind ( v , new_tile , dest_enterdir , tracks , & path_not_found , do_track_reservation , & res_dest ) ;
if ( new_tile = = tile ) best_track = next_track ;
/* handle "path not found" state */
if ( path_not_found ) {
/* PF didn't find the route */
if ( ! HasBit ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
/* it is first time the problem occurred, set the "path not found" flag */
SetBit ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
/* and notify user about the event */
2008-09-30 20:39:50 +00:00
if ( _settings_client . gui . lost_train_warn & & v - > owner = = _local_company ) {
2009-01-04 17:42:46 +00:00
SetDParam ( 0 , v - > index ) ;
2008-08-02 22:55:38 +00:00
AddNewsItem (
STR_TRAIN_IS_LOST ,
NS_ADVICE ,
v - > index ,
0 ) ;
}
}
} else {
/* route found, is the train marked with "path not found" flag? */
if ( HasBit ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ) {
/* clear the flag as the PF's problem was solved */
ClrBit ( v - > u . rail . flags , VRF_NO_PATH_TO_DESTINATION ) ;
/* can we also delete the "News" item somehow? */
2006-10-17 16:16:19 +00:00
}
}
}
2004-08-09 17:04:08 +00:00
2008-08-02 22:53:51 +00:00
/* No track reservation requested -> finished. */
if ( ! do_track_reservation ) return best_track ;
/* A path was found, but could not be reserved. */
if ( res_dest . tile ! = INVALID_TILE & & ! res_dest . okay ) {
if ( mark_stuck ) MarkTrainAsStuck ( v ) ;
FreeTrainTrackReservation ( v ) ;
return best_track ;
}
/* No possible reservation target found, we are probably lost. */
if ( res_dest . tile = = INVALID_TILE ) {
/* Try to find any safe destination. */
PBSTileInfo origin = FollowTrainReservation ( v ) ;
if ( TryReserveSafeTrack ( v , origin . tile , origin . trackdir , false ) ) {
TrackBits res = GetReservedTrackbits ( tile ) & DiagdirReachesTracks ( enterdir ) ;
best_track = FindFirstTrack ( res ) ;
TryReserveRailTrack ( v - > tile , TrackdirToTrack ( GetVehicleTrackdir ( v ) ) ) ;
if ( got_reservation ! = NULL ) * got_reservation = true ;
if ( changed_signal ) MarkTileDirtyByTile ( tile ) ;
} else {
FreeTrainTrackReservation ( v ) ;
if ( mark_stuck ) MarkTrainAsStuck ( v ) ;
}
return best_track ;
}
if ( got_reservation ! = NULL ) * got_reservation = true ;
2008-08-02 22:54:23 +00:00
/* Reservation target found and free, check if it is safe. */
while ( ! IsSafeWaitingPosition ( v , res_dest . tile , res_dest . trackdir , true , _settings_game . pf . forbid_90_deg ) ) {
/* Extend reservation until we have found a safe position. */
DiagDirection exitdir = TrackdirToExitdir ( res_dest . trackdir ) ;
TileIndex next_tile = TileAddByDiagDir ( res_dest . tile , exitdir ) ;
TrackBits reachable = TrackdirBitsToTrackBits ( ( TrackdirBits ) ( GetTileTrackStatus ( next_tile , TRANSPORT_RAIL , 0 ) ) ) & DiagdirReachesTracks ( exitdir ) ;
if ( _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . forbid_90_deg ) {
reachable & = ~ TrackCrossesTracks ( TrackdirToTrack ( res_dest . trackdir ) ) ;
}
/* Get next order with destination. */
2009-01-11 16:33:03 +00:00
if ( orders . SwitchToNextOrder ( true ) ) {
2008-08-02 22:54:23 +00:00
PBSTileInfo cur_dest ;
DoTrainPathfind ( v , next_tile , exitdir , reachable , NULL , true , & cur_dest ) ;
if ( cur_dest . tile ! = INVALID_TILE ) {
res_dest = cur_dest ;
if ( res_dest . okay ) continue ;
/* Path found, but could not be reserved. */
FreeTrainTrackReservation ( v ) ;
if ( mark_stuck ) MarkTrainAsStuck ( v ) ;
if ( got_reservation ! = NULL ) * got_reservation = false ;
changed_signal = false ;
break ;
}
}
/* No order or no safe position found, try any position. */
if ( ! TryReserveSafeTrack ( v , res_dest . tile , res_dest . trackdir , true ) ) {
FreeTrainTrackReservation ( v ) ;
if ( mark_stuck ) MarkTrainAsStuck ( v ) ;
if ( got_reservation ! = NULL ) * got_reservation = false ;
changed_signal = false ;
}
break ;
}
2008-08-02 22:53:51 +00:00
TryReserveRailTrack ( v - > tile , TrackdirToTrack ( GetVehicleTrackdir ( v ) ) ) ;
if ( changed_signal ) MarkTileDirtyByTile ( tile ) ;
2004-08-09 17:04:08 +00:00
return best_track ;
}
2008-08-02 22:54:38 +00:00
/**
* Try to reserve a path to a safe position .
*
* @ param v The vehicle
* @ return True if a path could be reserved
*/
2008-08-03 08:20:04 +00:00
bool TryPathReserve ( Vehicle * v , bool mark_as_stuck , bool first_tile_okay )
2008-08-02 22:54:38 +00:00
{
assert ( v - > type = = VEH_TRAIN & & IsFrontEngine ( v ) ) ;
/* We have to handle depots specially as the track follower won't look
* at the depot tile itself but starts from the next tile . If we are still
* inside the depot , a depot reservation can never be ours . */
if ( v - > u . rail . track & TRACK_BIT_DEPOT ) {
if ( GetDepotWaypointReservation ( v - > tile ) ) {
if ( mark_as_stuck ) MarkTrainAsStuck ( v ) ;
return false ;
} else {
/* Depot not reserved, but the next tile might be. */
TileIndex next_tile = TileAddByDiagDir ( v - > tile , GetRailDepotDirection ( v - > tile ) ) ;
if ( HasReservedTracks ( next_tile , DiagdirReachesTracks ( GetRailDepotDirection ( v - > tile ) ) ) ) return false ;
}
}
/* Special check if we are in front of a two-sided conventional signal. */
DiagDirection dir = TrainExitDir ( v - > direction , v - > u . rail . track ) ;
TileIndex next_tile = TileAddByDiagDir ( v - > tile , dir ) ;
if ( IsTileType ( next_tile , MP_RAILWAY ) & & HasReservedTracks ( next_tile , DiagdirReachesTracks ( dir ) ) ) {
/* Can have only one reserved trackdir. */
Trackdir td = FindFirstTrackdir ( ( TrackdirBits ) ( GetReservedTrackbits ( next_tile ) * 0x101 & DiagdirReachesTrackdirs ( dir ) ) ) ;
if ( HasSignalOnTrackdir ( next_tile , td ) & & HasSignalOnTrackdir ( next_tile , ReverseTrackdir ( td ) ) & &
! IsPbsSignal ( GetSignalType ( next_tile , TrackdirToTrack ( td ) ) ) ) {
/* Signal already reserved, is not ours. */
if ( mark_as_stuck ) MarkTrainAsStuck ( v ) ;
return false ;
}
}
2008-09-07 11:23:10 +00:00
bool other_train = false ;
2008-08-03 14:16:57 +00:00
PBSTileInfo origin = FollowTrainReservation ( v , & other_train ) ;
2008-08-02 22:54:38 +00:00
/* If we have a reserved path and the path ends at a safe tile, we are finished already. */
if ( origin . okay & & ( v - > tile ! = origin . tile | | first_tile_okay ) ) {
/* Can't be stuck then. */
if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
return true ;
}
2008-08-03 14:16:57 +00:00
/* The path we are driving on is alread blocked by some other train.
* This can only happen when tracks and signals are changed . A crash
* is probably imminent , don ' t do any further reservation because
* it might cause stale reservations . */
2008-09-07 11:23:10 +00:00
if ( other_train & & v - > tile ! = origin . tile ) {
2008-08-03 14:16:57 +00:00
if ( mark_as_stuck ) MarkTrainAsStuck ( v ) ;
return false ;
}
2008-08-02 22:54:38 +00:00
/* If we are in a depot, tentativly reserve the depot. */
if ( v - > u . rail . track & TRACK_BIT_DEPOT ) {
SetDepotWaypointReservation ( v - > tile , true ) ;
if ( _settings_client . gui . show_track_reservation ) MarkTileDirtyByTile ( v - > tile ) ;
}
DiagDirection exitdir = TrackdirToExitdir ( origin . trackdir ) ;
TileIndex new_tile = TileAddByDiagDir ( origin . tile , exitdir ) ;
TrackBits reachable = TrackdirBitsToTrackBits ( ( TrackdirBits ) GetTileTrackStatus ( new_tile , TRANSPORT_RAIL , 0 ) & DiagdirReachesTrackdirs ( exitdir ) ) ;
if ( _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . forbid_90_deg ) reachable & = ~ TrackCrossesTracks ( TrackdirToTrack ( origin . trackdir ) ) ;
bool res_made = false ;
ChooseTrainTrack ( v , new_tile , exitdir , reachable , true , & res_made , mark_as_stuck ) ;
if ( ! res_made ) {
/* Free the depot reservation as well. */
if ( v - > u . rail . track & TRACK_BIT_DEPOT ) SetDepotWaypointReservation ( v - > tile , false ) ;
return false ;
}
2009-01-05 20:28:56 +00:00
if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) {
v - > load_unload_time_rem = 0 ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
}
2008-08-02 22:54:38 +00:00
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
return true ;
}
2004-08-09 17:04:08 +00:00
static bool CheckReverseTrain ( Vehicle * v )
{
2008-05-29 15:13:28 +00:00
if ( _settings_game . difficulty . 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 | |
2008-04-07 12:36:50 +00:00
! ( v - > direction & 1 ) ) {
2004-08-09 17:04:08 +00:00
return false ;
2008-04-07 12:36:50 +00:00
}
2004-08-09 17:04:08 +00:00
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 ) ;
2008-05-29 15:13:28 +00:00
switch ( _settings_game . pf . pathfinder_for_trains ) {
2008-04-07 12:36:50 +00:00
case VPF_YAPF : /* YAPF */
2008-02-13 17:54:11 +00:00
reverse_best = YapfCheckReverseTrain ( v ) ;
2008-04-07 12:36:50 +00:00
break ;
2007-02-25 09:27:03 +00:00
2008-02-13 17:54:11 +00:00
case VPF_NPF : { /* NPF */
NPFFindStationOrTileData fstd ;
NPFFoundTargetData ftd ;
2008-04-07 12:36:50 +00:00
Vehicle * last = GetLastVehicleInChain ( v ) ;
2005-01-31 11:23:10 +00:00
2008-02-13 17:54:11 +00:00
NPFFillWithOrderData ( & fstd , v ) ;
2005-01-31 11:23:10 +00:00
2008-02-13 17:54:11 +00:00
Trackdir trackdir = GetVehicleTrackdir ( v ) ;
Trackdir trackdir_rev = ReverseTrackdir ( GetVehicleTrackdir ( last ) ) ;
assert ( trackdir ! = INVALID_TRACKDIR ) ;
assert ( trackdir_rev ! = INVALID_TRACKDIR ) ;
ftd = NPFRouteToStationOrTileTwoWay ( v - > tile , trackdir , false , last - > tile , trackdir_rev , false , & fstd , TRANSPORT_RAIL , 0 , v - > owner , v - > u . rail . compatible_railtypes ) ;
if ( ftd . best_bird_dist ! = 0 ) {
/* We didn't find anything, just keep on going straight ahead */
reverse_best = false ;
} else {
if ( NPFGetFlag ( & ftd . node , NPF_FLAG_REVERSE ) ) {
reverse_best = true ;
2004-08-09 17:04:08 +00:00
} else {
2008-02-13 17:54:11 +00:00
reverse_best = false ;
}
}
} break ;
default :
case VPF_NTP : { /* NTP */
2008-12-19 22:26:43 +00:00
TrainTrackFollowerData fd ;
FillWithStationData ( & fd , v ) ;
2008-04-01 14:47:57 +00:00
int i = _search_directions [ FindFirstTrack ( v - > u . rail . track ) ] [ DirToDiagDir ( v - > direction ) ] ;
2008-02-13 17:54:11 +00:00
int best_track = - 1 ;
uint reverse = 0 ;
uint best_bird_dist = 0 ;
uint best_track_dist = 0 ;
for ( ; ; ) {
fd . best_bird_dist = UINT_MAX ;
fd . best_track_dist = UINT_MAX ;
NewTrainPathfind ( v - > tile , v - > dest_tile , v - > u . rail . compatible_railtypes , ( DiagDirection ) ( reverse ^ i ) , ( NTPEnumProc * ) NtpCallbFindStation , & fd ) ;
if ( best_track ! = - 1 ) {
if ( best_bird_dist ! = 0 ) {
if ( fd . best_bird_dist ! = 0 ) {
/* neither reached the destination, pick the one with the smallest bird dist */
if ( fd . best_bird_dist > best_bird_dist ) goto bad ;
if ( fd . best_bird_dist < best_bird_dist ) goto good ;
} else {
/* we found the destination for the first time */
goto good ;
}
2005-01-31 11:23:10 +00:00
} else {
2008-02-13 17:54:11 +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 ;
}
2005-01-31 11:23:10 +00:00
}
2004-09-10 19:02:27 +00:00
2008-02-13 17:54:11 +00:00
/* if we reach this position, there's two paths of equal value so far.
* pick one randomly . */
int r = GB ( Random ( ) , 0 , 8 ) ;
if ( _pick_track_table [ i ] = = ( v - > direction & 3 ) ) r + = 80 ;
if ( _pick_track_table [ best_track ] = = ( v - > direction & 3 ) ) r - = 80 ;
if ( r < = 127 ) goto bad ;
}
2004-08-09 17:04:08 +00:00
good : ;
2008-02-13 17:54:11 +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 : ;
2008-02-13 17:54:11 +00:00
if ( reverse ! = 0 ) break ;
reverse = 2 ;
}
} break ;
2004-08-09 17:04:08 +00:00
}
return reverse_best ! = 0 ;
}
2008-04-05 10:55:50 +00:00
TileIndex Train : : GetOrderStationLocation ( StationID station )
2004-08-09 17:04:08 +00:00
{
2008-04-05 12:01:34 +00:00
if ( station = = this - > last_station_visited ) this - > last_station_visited = INVALID_STATION ;
2008-04-14 07:08:43 +00:00
const Station * st = GetStation ( station ) ;
if ( ! ( st - > facilities & FACIL_TRAIN ) ) {
/* The destination station has no trainstation tiles. */
this - > cur_order_index + + ;
return 0 ;
}
return st - > xy ;
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 ) ;
2008-01-16 21:17:31 +00:00
MarkSingleVehicleDirty ( v ) ;
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
}
2008-10-03 12:55:39 +00:00
/**
* This function looks at the vehicle and updates it ' s speed ( cur_speed
* and subspeed ) variables . Furthermore , it returns the distance that
* the train can drive this tick . This distance is expressed as 256 * n ,
* where n is the number of straight ( long ) tracks the train can
* traverse . This means that moving along a straight track costs 256
* " speed " and a diagonal track costs 192 " speed " .
* @ param v The vehicle to update the speed of .
* @ return distance to drive .
*/
2004-08-09 17:04:08 +00:00
static int UpdateTrainSpeed ( Vehicle * v )
{
uint accel ;
2008-08-02 22:53:21 +00:00
if ( v - > vehstatus & VS_STOPPED | | HasBit ( v - > u . rail . flags , VRF_REVERSING ) | | HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) {
2008-05-29 15:13:28 +00:00
if ( _settings_game . vehicle . 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 {
2008-05-29 15:13:28 +00:00
if ( _settings_game . vehicle . 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 ;
2007-11-19 18:38:10 +00:00
v - > cur_speed = spd = Clamp ( v - > cur_speed + ( ( int ) spd > > 8 ) , 0 , tempmax ) ;
2005-01-26 12:51:04 +00:00
}
2004-08-09 17:04:08 +00:00
2008-10-03 12:55:39 +00:00
/* Scale speed by 3/4. Previously this was only done when the train was
* facing diagonally and would apply to however many moves the train made
* regardless the of direction actually moved in . Now it is always scaled ,
* 256 spd is used to go straight and 192 is used to go diagonally
* ( 3 / 4 of 256 ) . This results in the same effect , but without the error the
* previous method caused .
*
* The scaling is done in this direction and not by multiplying the amount
* to be subtracted by 4 / 3 so that the leftover speed can be saved in a
* byte in v - > progress .
*/
int scaled_spd = spd * 3 > > 2 ;
2004-08-09 17:04:08 +00:00
2008-10-03 12:55:39 +00:00
scaled_spd + = v - > progress ;
v - > progress = 0 ; // set later in TrainLocoHandler or TrainController
return scaled_spd ;
2004-08-09 17:04:08 +00:00
}
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 ) ;
2004-08-09 17:04:08 +00:00
AddNewsItem (
STR_8801_CITIZENS_CELEBRATE_FIRST ,
2008-09-30 20:39:50 +00:00
v - > owner = = _local_company ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER ,
2004-08-09 17:04:08 +00:00
v - > index ,
2008-09-13 10:19:51 +00:00
st - > index
2006-02-13 21:15:00 +00:00
) ;
2009-01-12 17:11:45 +00:00
AI : : NewEvent ( v - > owner , new AIEventStationFirstVehicle ( st - > index , v - > index ) ) ;
2004-08-09 17:04:08 +00:00
}
2007-04-29 18:21:24 +00:00
v - > BeginLoading ( ) ;
2008-04-19 23:19:12 +00:00
StationAnimationTrigger ( st , v - > tile , STAT_ANIM_TRAIN_ARRIVES ) ;
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 ) {
2007-11-19 21:32:20 +00:00
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-11-20 13:35:54 +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
} ;
2008-02-20 12:07:22 +00:00
static inline 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 ] ;
}
2008-02-20 12:07:22 +00:00
static inline 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 */
2008-02-20 12:07:22 +00:00
static inline 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-11-19 21:02:30 +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 */
2008-04-07 12:36:50 +00:00
static inline void AffectSpeedByDirChange ( Vehicle * v , Direction new_dir )
2004-08-09 17:04:08 +00:00
{
2008-05-29 15:13:28 +00:00
if ( _settings_game . vehicle . realistic_acceleration ) return ;
2006-03-08 07:48:56 +00:00
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 */
2008-02-20 12:07:22 +00:00
static inline void AffectSpeedByZChange ( Vehicle * v , byte old_z )
2004-08-09 17:04:08 +00:00
{
2008-05-29 15:13:28 +00:00
if ( old_z = = v - > z_pos | | _settings_game . vehicle . 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
}
}
2008-08-02 22:56:35 +00:00
static bool 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 ) {
2008-04-01 14:47:57 +00:00
TrackdirBits tracks = TrackBitsToTrackdirBits ( GetTrackBits ( tile ) ) & DiagdirReachesTrackdirs ( dir ) ;
Trackdir trackdir = FindFirstTrackdir ( tracks ) ;
2008-08-02 22:56:35 +00:00
if ( UpdateSignalsOnSegment ( tile , TrackdirToExitdir ( trackdir ) , GetTileOwner ( tile ) ) = = SIGSEG_PBS & & HasSignalOnTrackdir ( tile , trackdir ) ) {
/* A PBS block with a non-PBS signal facing us? */
if ( ! IsPbsSignal ( GetSignalType ( tile , TrackdirToTrack ( trackdir ) ) ) ) return true ;
}
2004-08-09 17:04:08 +00:00
}
2008-08-02 22:56:35 +00:00
return false ;
2004-08-09 17:04:08 +00:00
}
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
2008-08-02 22:54:07 +00:00
/* Free a possible path reservation and try to mark all tiles occupied by the train reserved. */
if ( IsFrontEngine ( v ) ) {
/* Remove all reservations, also the ones currently under the train
* and any railway station paltform reservation . */
FreeTrainTrackReservation ( v ) ;
for ( const Vehicle * u = v ; u ! = NULL ; u = u - > Next ( ) ) {
2008-09-09 19:02:47 +00:00
ClearPathReservation ( u , u - > tile , GetVehicleTrackdir ( u ) ) ;
2008-08-04 13:15:15 +00:00
if ( IsTileType ( u - > tile , MP_TUNNELBRIDGE ) ) {
/* ClearPathReservation will not free the wormhole exit
* if the train has just entered the wormhole . */
SetTunnelBridgeReservation ( GetOtherTunnelBridgeEnd ( u - > tile ) , false ) ;
}
2008-08-02 22:54:07 +00:00
}
}
2008-01-17 17:57:39 +00:00
/* we may need to update crossing we were approaching */
TileIndex crossing = TrainApproachingCrossingTile ( v ) ;
2004-08-09 17:04:08 +00:00
v - > u . rail . crash_anim_pos + + ;
2004-09-10 19:02:27 +00:00
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
2008-01-13 14:42:28 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , v - > index ) ;
if ( v - > u . rail . track = = TRACK_BIT_DEPOT ) {
InvalidateWindow ( WC_VEHICLE_DEPOT , v - > tile ) ;
}
2008-05-18 16:51:44 +00:00
InvalidateWindowClassesData ( WC_TRAINS_LIST , 0 ) ;
2008-01-13 14:42:28 +00:00
2008-05-21 12:06:05 +00:00
for ( ; v ! = NULL ; v = v - > Next ( ) ) {
2004-08-09 17:04:08 +00:00
v - > vehstatus | = VS_CRASHED ;
2008-01-16 21:17:31 +00:00
MarkSingleVehicleDirty ( v ) ;
2008-05-21 12:06:05 +00:00
}
2008-01-17 17:57:39 +00:00
/* must be updated after the train has been marked crashed */
2008-01-17 19:49:06 +00:00
if ( crossing ! = INVALID_TILE ) UpdateLevelCrossing ( crossing ) ;
2004-08-09 17:04:08 +00:00
}
2008-04-07 12:36:50 +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 ;
2008-05-21 12:06:05 +00:00
for ( ; v ! = NULL ; v = v - > Next ( ) ) {
2007-06-22 11:58:59 +00:00
if ( IsCargoInClass ( v - > cargo_type , CC_PASSENGERS ) ) num + = v - > cargo . Count ( ) ;
2008-05-21 12:06:05 +00:00
}
2004-08-09 17:04:08 +00:00
return num ;
}
2007-06-19 16:40:59 +00:00
struct TrainCollideChecker {
2008-04-07 12:36:50 +00:00
Vehicle * v ; ///< vehicle we are testing for collision
uint num ; ///< number of dead if train collided
2007-06-19 16:40:59 +00:00
} ;
2008-08-01 15:07:31 +00:00
static Vehicle * FindTrainCollideEnum ( Vehicle * v , void * data )
2007-06-19 16:40:59 +00:00
{
2008-04-07 12:36:50 +00:00
TrainCollideChecker * tcc = ( TrainCollideChecker * ) data ;
2007-06-19 16:40:59 +00:00
2008-01-13 23:53:33 +00:00
if ( v - > type ! = VEH_TRAIN ) return NULL ;
/* get first vehicle now to make most usual checks faster */
Vehicle * coll = v - > First ( ) ;
2008-01-16 15:48:36 +00:00
/* can't collide with own wagons && can't crash in depot && the same height level */
if ( coll ! = tcc - > v & & v - > u . rail . track ! = TRACK_BIT_DEPOT & & abs ( v - > z_pos - tcc - > v - > z_pos ) < 6 ) {
int x_diff = v - > x_pos - tcc - > v - > x_pos ;
int y_diff = v - > y_pos - tcc - > v - > y_pos ;
/* needed to disable possible crash of competitor train in station by building diagonal track at its end */
if ( x_diff * x_diff + y_diff * y_diff > 25 ) return NULL ;
2007-06-19 16:40:59 +00:00
if ( ! ( tcc - > v - > vehstatus & VS_CRASHED ) ) {
2008-01-16 15:48:36 +00:00
/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
2007-06-19 16:40:59 +00:00
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 ) ;
}
2008-08-03 17:16:39 +00:00
/* Try to reserve all tiles directly under the crashed trains.
* As there might be more than two trains involved , we have to do that for all vehicles */
const Vehicle * u ;
FOR_ALL_VEHICLES ( u ) {
2008-08-04 13:15:15 +00:00
if ( u - > type = = VEH_TRAIN & & HASBITS ( u - > vehstatus , VS_CRASHED ) & & ( u - > u . rail . track & TRACK_BIT_DEPOT ) = = TRACK_BIT_NONE ) {
TrackBits trackbits = u - > u . rail . track ;
if ( ( trackbits & TRACK_BIT_WORMHOLE ) = = TRACK_BIT_WORMHOLE ) {
/* Vehicle is inside a wormhole, v->u.rail.track contains no useful value then. */
trackbits | = DiagDirToDiagTrackBits ( GetTunnelBridgeDirection ( u - > tile ) ) ;
}
2008-08-05 13:25:49 +00:00
TryReserveRailTrack ( u - > tile , TrackBitsToTrack ( trackbits ) ) ;
2008-08-03 17:16:39 +00:00
}
}
2007-06-19 16:40:59 +00:00
}
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 .
*/
2008-10-22 23:06:36 +00:00
static bool CheckTrainCollision ( Vehicle * v )
2004-08-09 17:04:08 +00:00
{
/* can't collide in depot */
2008-10-22 23:06:36 +00:00
if ( v - > u . rail . track = = TRACK_BIT_DEPOT ) return false ;
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-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 ) {
2008-09-07 11:23:10 +00:00
FindVehicleOnPos ( v - > tile , & tcc , FindTrainCollideEnum ) ;
FindVehicleOnPos ( GetOtherTunnelBridgeEnd ( v - > tile ) , & tcc , FindTrainCollideEnum ) ;
2007-07-30 08:49:41 +00:00
} else {
2008-09-07 11:23:10 +00:00
FindVehicleOnPosXY ( v - > x_pos , v - > y_pos , & tcc , FindTrainCollideEnum ) ;
2007-07-30 08:49:41 +00:00
}
2004-08-09 17:04:08 +00:00
2007-06-19 16:40:59 +00:00
/* any dead -> no crash */
2008-10-22 23:06:36 +00:00
if ( tcc . num = = 0 ) return false ;
2004-09-10 19:02:27 +00:00
2009-01-12 17:11:45 +00:00
AI : : NewEvent ( v - > owner , new AIEventVehicleCrashed ( v - > index , v - > tile ) ) ;
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 ,
2008-05-15 13:39:36 +00:00
NS_ACCIDENT_VEHICLE ,
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 ) ;
2008-10-22 23:06:36 +00:00
return true ;
2004-08-09 17:04:08 +00:00
}
2008-08-01 15:07:31 +00:00
static Vehicle * CheckVehicleAtSignal ( Vehicle * v , void * data )
2004-08-09 17:04:08 +00:00
{
2008-07-23 18:23:12 +00:00
DiagDirection exitdir = * ( DiagDirection * ) data ;
2005-03-06 12:41:18 +00:00
2008-08-01 13:25:19 +00:00
/* front engine of a train, not inside wormhole or depot, not crashed */
if ( v - > type = = VEH_TRAIN & & IsFrontEngine ( v ) & & ( v - > u . rail . track & TRACK_BIT_MASK ) ! = 0 & & ! ( v - > vehstatus & VS_CRASHED ) ) {
2008-07-23 18:23:12 +00:00
if ( v - > cur_speed < = 5 & & TrainExitDir ( v - > direction , v - > u . rail . track ) = = exitdir ) return v ;
2004-08-09 17:04:08 +00:00
}
2008-07-23 18:23:12 +00:00
2005-03-06 12:41:18 +00:00
return NULL ;
2004-08-09 17:04:08 +00:00
}
2008-03-19 20:42:05 +00:00
static void TrainController ( Vehicle * v , Vehicle * nomove , 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 */
2008-03-19 20:42:05 +00:00
for ( prev = v - > Previous ( ) ; v ! = nomove ; prev = v , v = v - > Next ( ) ) {
2007-06-12 11:22:32 +00:00
DiagDirection enterdir = DIAGDIR_BEGIN ;
2008-01-17 17:57:39 +00:00
bool update_signals_crossing = false ; // will we update signals or crossing state?
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
2009-01-10 15:27:57 +00:00
/* Reverse when we are at the end of the track already, do not move to the new position */
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-11-19 21:02:30 +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-11-19 21:02:30 +00:00
if ( HasBit ( r , VETS_ENTERED_STATION ) ) {
2009-01-10 15:27:57 +00:00
/* The new position is the end of the platform */
2007-02-13 10:26:53 +00:00
TrainEnterStation ( v , r > > VETS_STATION_ID_OFFSET ) ;
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 . */
2008-04-01 14:47:57 +00:00
TrackStatus ts = GetTileTrackStatus ( gp . new_tile , TRANSPORT_RAIL , 0 , ReverseDiagDir ( enterdir ) ) ;
TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs ( enterdir ) ;
TrackdirBits trackdirbits = TrackStatusToTrackdirBits ( ts ) & reachable_trackdirs ;
TrackBits red_signals = TrackdirBitsToTrackBits ( TrackStatusToRedSignals ( ts ) & reachable_trackdirs ) ;
2004-08-09 17:04:08 +00:00
2008-02-20 17:49:50 +00:00
TrackBits bits = TrackdirBitsToTrackBits ( trackdirbits ) ;
2008-05-29 15:13:28 +00:00
if ( _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . 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 */
2008-08-02 22:53:51 +00:00
chosen_track = TrackToTrackBits ( ChooseTrainTrack ( v , gp . new_tile , enterdir , bits , false , NULL , true ) ) ;
assert ( chosen_track & ( bits | GetReservedTrackbits ( gp . new_tile ) ) ) ;
2004-08-09 17:04:08 +00:00
/* Check if it's a red signal and that force proceed is not clicked. */
2008-02-20 17:49:50 +00:00
if ( red_signals & chosen_track & & v - > u . rail . force_proceed = = 0 ) {
/* In front of a red signal */
Trackdir i = FindFirstTrackdir ( trackdirbits ) ;
2007-02-25 09:47:46 +00:00
2008-08-02 22:53:21 +00:00
/* Don't handle stuck trains here. */
if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) return ;
2007-02-25 09:47:46 +00:00
if ( ! HasSignalOnTrackdir ( gp . new_tile , ReverseTrackdir ( i ) ) ) {
v - > cur_speed = 0 ;
v - > subspeed = 0 ;
v - > progress = 255 - 100 ;
2009-01-05 20:29:05 +00:00
if ( _settings_game . pf . wait_oneway_signal = = 255 | | + + v - > load_unload_time_rem < _settings_game . pf . wait_oneway_signal * 20 ) return ;
2007-02-25 09:47:46 +00:00
} 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 ;
2009-01-05 20:29:05 +00:00
if ( _settings_game . pf . wait_twoway_signal = = 255 | | + + v - > load_unload_time_rem < _settings_game . pf . wait_twoway_signal * 73 ) {
2008-07-23 18:23:12 +00:00
DiagDirection exitdir = TrackdirToExitdir ( i ) ;
TileIndex o_tile = TileAddByDiagDir ( gp . new_tile , exitdir ) ;
exitdir = ReverseDiagDir ( exitdir ) ;
2007-02-25 09:47:46 +00:00
/* check if a train is waiting on the other side */
2008-09-07 11:23:10 +00:00
if ( ! HasVehicleOnPos ( o_tile , & exitdir , & CheckVehicleAtSignal ) ) return ;
2007-02-25 09:47:46 +00:00
}
}
2008-08-02 22:56:21 +00:00
/* If we would reverse but are currently in a PBS block and
* reversing of stuck trains is disabled , don ' t reverse . */
if ( _settings_game . pf . wait_for_pbs_path = = 255 & & UpdateSignalsOnSegment ( v - > tile , enterdir , v - > owner ) = = SIGSEG_PBS ) {
v - > load_unload_time_rem = 0 ;
return ;
}
2007-02-25 09:47:46 +00:00
goto reverse_train_direction ;
2008-08-10 13:37:09 +00:00
} else {
TryReserveRailTrack ( gp . new_tile , TrackBitsToTrack ( chosen_track ) ) ;
2007-02-25 09:47:46 +00:00
}
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-11-19 21:02:30 +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-11-19 21:02:30 +00:00
if ( ! HasBit ( r , VETS_ENTERED_WORMHOLE ) ) {
2008-08-02 22:53:37 +00:00
Track track = FindFirstTrack ( chosen_track ) ;
Trackdir tdir = TrackDirectionToTrackdir ( track , chosen_dir ) ;
if ( IsFrontEngine ( v ) & & HasPbsSignalOnTrackdir ( gp . new_tile , tdir ) ) {
SetSignalStateByTrackdir ( gp . new_tile , tdir , SIGNAL_STATE_RED ) ;
MarkTileDirtyByTile ( gp . new_tile ) ;
}
/* Clear any track reservation when the last vehicle leaves the tile */
2008-09-09 19:02:47 +00:00
if ( v - > Next ( ) = = NULL ) ClearPathReservation ( v , v - > tile , GetVehicleTrackdir ( v ) ) ;
2008-08-02 22:53:37 +00:00
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 ( ) */
2008-01-17 17:57:39 +00:00
update_signals_crossing = 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 ;
2008-08-02 22:55:23 +00:00
if ( IsFrontEngine ( v ) ) {
v - > load_unload_time_rem = 0 ;
2008-08-02 22:57:18 +00:00
/* If we are approching a crossing that is reserved, play the sound now. */
TileIndex crossing = TrainApproachingCrossingTile ( v ) ;
if ( crossing ! = INVALID_TILE & & GetCrossingReservation ( crossing ) ) SndPlayTileFx ( SND_0E_LEVEL_CROSSING , crossing ) ;
2008-08-02 22:55:23 +00:00
/* Always try to extend the reservation when entering a tile. */
CheckNextTrainTile ( v ) ;
}
2004-08-09 17:04:08 +00:00
}
} else {
2007-12-27 13:25:23 +00:00
/* In a tunnel or on a bridge
* - for tunnels , only the part when the vehicle is not visible ( part of enter / exit tile too )
* - for bridges , only the middle part - without the bridge heads */
2007-02-24 18:44:30 +00:00
if ( ! ( v - > vehstatus & VS_HIDDEN ) ) {
v - > cur_speed =
2008-02-05 05:21:02 +00:00
min ( v - > cur_speed , GetBridgeSpec ( GetBridgeType ( v - > tile ) ) - > speed ) ;
2007-02-24 18:44:30 +00:00
}
2006-12-27 12:38:02 +00:00
2008-08-02 22:55:23 +00:00
if ( IsTileType ( gp . new_tile , MP_TUNNELBRIDGE ) & & HasBit ( VehicleEnterTile ( v , gp . new_tile , gp . x , gp . y ) , VETS_ENTERED_WORMHOLE ) ) {
/* Perform look-ahead on tunnel exit. */
2008-08-10 13:37:09 +00:00
if ( IsFrontEngine ( v ) ) {
TryReserveRailTrack ( gp . new_tile , DiagDirToDiagTrack ( GetTunnelBridgeDirection ( gp . new_tile ) ) ) ;
CheckNextTrainTile ( v ) ;
}
2008-08-02 22:55:23 +00:00
} else {
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-12-27 13:25:23 +00:00
v - > UpdateDeltaXY ( v - > direction ) ;
if ( update_image ) v - > cur_image = v - > GetImage ( v - > direction ) ;
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
2008-01-17 17:57:39 +00:00
if ( update_signals_crossing ) {
2008-08-02 22:56:35 +00:00
if ( IsFrontEngine ( v ) ) {
if ( TrainMovedChangeSignals ( gp . new_tile , enterdir ) ) {
/* We are entering a block with PBS signals right now, but
* not through a PBS signal . This means we don ' t have a
* reservation right now . As a conventional signal will only
* ever be green if no other train is in the block , getting
* a path should always be possible . If the player built
* such a strange network that it is not possible , the train
* will be marked as stuck and the player has to deal with
* the problem . */
if ( ( ! HasReservedTracks ( gp . new_tile , v - > u . rail . track ) & &
! TryReserveRailTrack ( gp . new_tile , FindFirstTrack ( v - > u . rail . track ) ) ) | |
! TryPathReserve ( v ) ) {
MarkTrainAsStuck ( v ) ;
}
}
}
2007-06-12 11:22:32 +00:00
/* Signals can only change when the first
* ( above ) or the last vehicle moves . */
2008-01-17 17:57:39 +00:00
if ( v - > Next ( ) = = NULL ) {
TrainMovedChangeSignals ( gp . old_tile , ReverseDiagDir ( enterdir ) ) ;
2008-01-17 19:49:06 +00:00
if ( IsLevelCrossingTile ( gp . old_tile ) ) UpdateLevelCrossing ( gp . old_tile ) ;
2008-01-17 17:57:39 +00:00
}
2007-06-12 11:22:32 +00:00
}
2008-08-02 22:55:23 +00:00
/* Do not check on every tick to save some computing time. */
if ( IsFrontEngine ( v ) & & v - > tick_counter % _settings_game . pf . path_backoff_interval = = 0 ) CheckNextTrainTile ( v ) ;
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?? */
2008-06-05 20:54:52 +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 ) ;
}
2008-08-03 19:04:11 +00:00
/** Collect trackbits of all crashed train vehicles on a tile
2008-09-07 11:23:10 +00:00
* @ param v Vehicle passed from Find / HasVehicleOnPos ( )
2008-08-03 19:04:11 +00:00
* @ param data trackdirbits for the result
2008-09-07 11:23:10 +00:00
* @ return NULL to iterate over all vehicles on the tile .
2008-08-03 19:04:11 +00:00
*/
static Vehicle * CollectTrackbitsFromCrashedVehiclesEnum ( Vehicle * v , void * data )
{
TrackBits * trackbits = ( TrackBits * ) data ;
2008-08-04 13:15:15 +00:00
if ( v - > type = = VEH_TRAIN & & ( v - > vehstatus & VS_CRASHED ) ! = 0 ) {
if ( ( v - > u . rail . track & TRACK_BIT_WORMHOLE ) = = TRACK_BIT_WORMHOLE ) {
/* Vehicle is inside a wormhole, v->u.rail.track contains no useful value then. */
* trackbits | = DiagDirToDiagTrackBits ( GetTunnelBridgeDirection ( v - > tile ) ) ;
} else {
* trackbits | = v - > u . rail . track ;
}
}
2008-08-03 19:04:11 +00:00
return NULL ;
}
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 ,
2007-12-15 00:04:01 +00:00
* or inside a tunnel / bridge , 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 )
{
2008-01-13 13:11:59 +00:00
Vehicle * first = v - > First ( ) ;
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
2008-12-26 21:08:51 +00:00
if ( first ! = v ) {
2008-01-13 13:11:59 +00:00
/* Recalculate cached train properties */
2008-07-24 15:19:26 +00:00
TrainConsistChanged ( first , false ) ;
2008-01-13 13:11:59 +00:00
/* Update the depot window if the first vehicle is in depot -
* if v = = first , then it is updated in PreDestructor ( ) */
if ( first - > u . rail . track = = TRACK_BIT_DEPOT ) {
InvalidateWindow ( WC_VEHICLE_DEPOT , first - > tile ) ;
}
2008-01-12 19:24:58 +00:00
}
2004-08-09 17:04:08 +00:00
2008-01-12 19:20:44 +00:00
/* 'v' shouldn't be accessed after it has been deleted */
2008-08-03 19:04:11 +00:00
TrackBits trackbits = v - > u . rail . track ;
2008-01-12 19:20:44 +00:00
TileIndex tile = v - > tile ;
2008-01-15 15:00:01 +00:00
Owner owner = v - > owner ;
2008-01-12 19:20:44 +00:00
2007-08-03 19:36:00 +00:00
delete v ;
2008-08-03 19:04:11 +00:00
v = NULL ; // make sure nobody will try to read 'v' anymore
2008-08-04 13:15:15 +00:00
if ( ( trackbits & TRACK_BIT_WORMHOLE ) = = TRACK_BIT_WORMHOLE ) {
/* Vehicle is inside a wormhole, v->u.rail.track contains no useful value then. */
trackbits | = DiagDirToDiagTrackBits ( GetTunnelBridgeDirection ( tile ) ) ;
}
Track track = TrackBitsToTrack ( trackbits ) ;
2008-08-03 19:04:11 +00:00
if ( HasReservedTracks ( tile , trackbits ) ) {
UnreserveRailTrack ( tile , track ) ;
/* If there are still crashed vehicles on the tile, give the track reservation to them */
TrackBits remaining_trackbits = TRACK_BIT_NONE ;
2008-09-07 11:23:10 +00:00
FindVehicleOnPos ( tile , & remaining_trackbits , CollectTrackbitsFromCrashedVehiclesEnum ) ;
2008-08-03 19:04:11 +00:00
/* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */
assert ( TRACK_BEGIN = = TRACK_X & & TRACK_Y = = TRACK_BEGIN + 1 ) ;
for ( Track t = TRACK_BEGIN ; t < TRACK_END ; t + + ) {
if ( HasBit ( remaining_trackbits , t ) ) {
TryReserveRailTrack ( tile , t ) ;
}
}
}
2004-08-09 17:04:08 +00:00
2008-01-17 17:57:39 +00:00
/* check if the wagon was on a road/rail-crossing */
2008-01-17 19:49:06 +00:00
if ( IsLevelCrossingTile ( tile ) ) UpdateLevelCrossing ( tile ) ;
2005-01-23 10:40:54 +00:00
2008-01-12 19:33:25 +00:00
/* Update signals */
2008-04-17 18:24:45 +00:00
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) | | IsRailDepotTile ( tile ) ) {
2008-01-15 15:00:01 +00:00
UpdateSignalsOnSegment ( tile , INVALID_DIAGDIR , owner ) ;
2008-01-12 19:33:25 +00:00
} else {
2008-08-03 19:04:11 +00:00
SetSignalsOnBothDir ( tile , track , owner ) ;
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
2008-04-07 12:36:50 +00:00
* 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 ;
2007-11-25 15:35:25 +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
2008-04-18 04:37:06 +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 ) ) {
2008-05-29 15:13:28 +00:00
SndPlayVehicleFx ( ( _settings_game . game_creation . 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 ) ;
2008-04-20 10:13:54 +00:00
if ( u ! = NULL ) u - > u . effect . 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 ) ;
}
}
}
2008-01-14 14:46:09 +00:00
/** Maximum speeds for train that is broken down or approaching line end */
2008-01-16 15:48:36 +00:00
static const uint16 _breakdown_speeds [ 16 ] = {
2004-08-09 17:04:08 +00:00
225 , 210 , 195 , 180 , 165 , 150 , 135 , 120 , 105 , 90 , 75 , 60 , 45 , 30 , 15 , 15
} ;
2008-01-16 00:33:28 +00:00
/**
* Train is approaching line end , slow down and possibly reverse
*
* @ param v front train engine
* @ param signal not line end , just a red signal
* @ return true iff we did NOT have to reverse
*/
static bool TrainApproachingLineEnd ( Vehicle * v , bool signal )
{
/* Calc position within the current tile */
uint x = v - > x_pos & 0xF ;
uint y = v - > y_pos & 0xF ;
2008-01-16 15:48:36 +00:00
/* for diagonal directions, 'x' will be 0..15 -
* for other directions , it will be 1 , 3 , 5 , . . . , 15 */
2008-01-16 00:33:28 +00:00
switch ( v - > direction ) {
2008-01-16 15:48:36 +00:00
case DIR_N : x = ~ x + ~ y + 25 ; break ;
2008-01-16 00:33:28 +00:00
case DIR_NW : x = y ; /* FALLTHROUGH */
case DIR_NE : x = ~ x + 16 ; break ;
2008-01-16 15:48:36 +00:00
case DIR_E : x = ~ x + y + 9 ; break ;
2008-01-16 00:33:28 +00:00
case DIR_SE : x = y ; break ;
2008-01-16 15:48:36 +00:00
case DIR_S : x = x + y - 7 ; break ;
case DIR_W : x = ~ y + x + 9 ; break ;
2008-01-16 00:33:28 +00:00
default : break ;
}
/* do not reverse when approaching red signal */
2008-10-24 20:53:57 +00:00
if ( ! signal & & x + ( v - > u . rail . cached_veh_length + 1 ) / 2 > = TILE_SIZE ) {
2008-01-16 00:33:28 +00:00
/* we are too near the tile end, reverse now */
v - > cur_speed = 0 ;
ReverseTrainDirection ( v ) ;
return false ;
}
/* slow down */
v - > vehstatus | = VS_TRAIN_SLOWING ;
uint16 break_speed = _breakdown_speeds [ x & 0xF ] ;
if ( break_speed < v - > cur_speed ) v - > cur_speed = break_speed ;
return true ;
}
2008-01-17 17:57:39 +00:00
/**
* Determines whether train would like to leave the tile
* @ param v train to test
* @ return true iff vehicle is NOT entering or inside a depot or tunnel / bridge
*/
static bool TrainCanLeaveTile ( const Vehicle * v )
{
/* Exit if inside a tunnel/bridge or a depot */
if ( v - > u . rail . track = = TRACK_BIT_WORMHOLE | | v - > u . rail . track = = TRACK_BIT_DEPOT ) return false ;
TileIndex tile = v - > tile ;
/* entering a tunnel/bridge? */
if ( IsTileType ( tile , MP_TUNNELBRIDGE ) ) {
DiagDirection dir = GetTunnelBridgeDirection ( tile ) ;
if ( DiagDirToDir ( dir ) = = v - > direction ) return false ;
}
/* entering a depot? */
2008-04-17 18:24:45 +00:00
if ( IsRailDepotTile ( tile ) ) {
2008-01-17 17:57:39 +00:00
DiagDirection dir = ReverseDiagDir ( GetRailDepotDirection ( tile ) ) ;
if ( DiagDirToDir ( dir ) = = v - > direction ) return false ;
}
return true ;
}
/**
* Determines whether train is approaching a rail - road crossing
* ( thus making it barred )
* @ param v front engine of train
* @ return TileIndex of crossing the train is approaching , else INVALID_TILE
* @ pre v in non - crashed front engine
*/
static TileIndex TrainApproachingCrossingTile ( const Vehicle * v )
{
assert ( IsFrontEngine ( v ) ) ;
assert ( ! ( v - > vehstatus & VS_CRASHED ) ) ;
if ( ! TrainCanLeaveTile ( v ) ) return INVALID_TILE ;
DiagDirection dir = TrainExitDir ( v - > direction , v - > u . rail . track ) ;
TileIndex tile = v - > tile + TileOffsByDiagDir ( dir ) ;
2008-02-20 11:00:17 +00:00
/* not a crossing || wrong axis || unusable rail (wrong type or owner) */
2008-01-17 17:57:39 +00:00
if ( ! IsLevelCrossingTile ( tile ) | | DiagDirToAxis ( dir ) = = GetCrossingRoadAxis ( tile ) | |
2008-02-20 11:00:17 +00:00
! CheckCompatibleRail ( v , tile ) ) {
2008-01-17 17:57:39 +00:00
return INVALID_TILE ;
}
return tile ;
}
2008-01-14 14:46:09 +00:00
/**
* Checks for line end . Also , bars crossing at next tile if needed
*
* @ param v vehicle we are checking
2008-01-16 00:33:28 +00:00
* @ return true iff we did NOT have to reverse
2008-01-14 14:46:09 +00:00
*/
2004-12-21 16:00:14 +00:00
static bool TrainCheckIfLineEnds ( Vehicle * v )
2004-08-09 17:04:08 +00:00
{
2008-01-16 00:33:28 +00:00
/* First, handle broken down train */
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 ;
}
2008-01-17 17:57:39 +00:00
if ( ! TrainCanLeaveTile ( v ) ) 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 */
2008-01-11 15:10:59 +00:00
DiagDirection dir = TrainExitDir ( v - > direction , v - > u . rail . track ) ;
2004-08-11 22:07:08 +00:00
/* Calculate next tile */
2008-01-17 17:57:39 +00:00
TileIndex tile = v - > tile + TileOffsByDiagDir ( dir ) ;
2008-01-16 00:33:28 +00:00
/* Determine the track status on the next tile */
2008-04-01 14:47:57 +00:00
TrackStatus ts = GetTileTrackStatus ( tile , TRANSPORT_RAIL , 0 , ReverseDiagDir ( dir ) ) ;
TrackdirBits reachable_trackdirs = DiagdirReachesTrackdirs ( dir ) ;
TrackdirBits trackdirbits = TrackStatusToTrackdirBits ( ts ) & reachable_trackdirs ;
TrackdirBits red_signals = TrackStatusToRedSignals ( ts ) & reachable_trackdirs ;
2004-09-10 19:02:27 +00:00
2008-01-16 00:33:28 +00:00
/* We are sure the train is not entering a depot, it is detected above */
2004-09-10 19:02:27 +00:00
2008-02-04 15:56:39 +00:00
/* mask unreachable track bits if we are forbidden to do 90deg turns */
2008-02-20 17:49:50 +00:00
TrackBits bits = TrackdirBitsToTrackBits ( trackdirbits ) ;
2008-05-29 15:13:28 +00:00
if ( _settings_game . pf . pathfinder_for_trains ! = VPF_NTP & & _settings_game . pf . forbid_90_deg ) {
2008-02-04 15:56:39 +00:00
bits & = ~ TrackCrossesTracks ( FindFirstTrack ( v - > u . rail . track ) ) ;
}
2008-02-20 11:00:17 +00:00
/* no suitable trackbits at all || unusable rail (wrong type or owner) */
if ( bits = = TRACK_BIT_NONE | | ! CheckCompatibleRail ( v , tile ) ) {
2008-01-16 00:33:28 +00:00
return TrainApproachingLineEnd ( v , false ) ;
2004-08-09 17:04:08 +00:00
}
2008-01-16 00:33:28 +00:00
/* approaching red signal */
2008-02-20 17:49:50 +00:00
if ( ( trackdirbits & red_signals ) ! = 0 ) return TrainApproachingLineEnd ( v , true ) ;
2004-08-09 17:04:08 +00:00
2008-01-16 00:33:28 +00:00
/* approaching a rail/road crossing? then make it red */
2008-01-18 21:44:20 +00:00
if ( IsLevelCrossingTile ( tile ) ) MaybeBarCrossingWithSound ( tile ) ;
2004-12-21 16:00:14 +00:00
return true ;
2004-08-09 17:04:08 +00:00
}
2008-01-16 00:33:28 +00:00
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 ;
}
2008-08-02 22:53:21 +00:00
if ( v - > u . rail . force_proceed ! = 0 ) {
v - > u . rail . force_proceed - - ;
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
}
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 ;
}
2008-04-19 21:23:42 +00:00
if ( ! v - > current_order . IsType ( OT_LOADING ) ) v - > breakdown_ctr - - ;
2004-08-09 17:04:08 +00:00
}
2007-11-19 21:02:30 +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
2008-08-02 22:55:23 +00:00
bool valid_order = v - > current_order . IsValid ( ) & & v - > current_order . GetType ( ) ! = OT_CONDITIONAL ;
2008-04-05 10:55:50 +00:00
if ( ProcessOrders ( v ) & & CheckReverseTrain ( v ) ) {
2004-08-09 17:04:08 +00:00
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
2008-04-05 23:36:54 +00:00
if ( v - > current_order . IsType ( 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 ) ;
2008-08-02 22:55:23 +00:00
/* We had no order but have an order now, do look ahead. */
if ( ! valid_order & & v - > current_order . IsValid ( ) ) {
CheckNextTrainTile ( v ) ;
}
2008-08-02 22:54:38 +00:00
/* Handle stuck trains. */
if ( ! mode & & HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ) {
+ + v - > load_unload_time_rem ;
/* Should we try reversing this tick if still stuck? */
bool turn_around = v - > load_unload_time_rem % ( _settings_game . pf . wait_for_pbs_path * DAY_TICKS ) = = 0 & & _settings_game . pf . wait_for_pbs_path < 255 ;
2008-08-02 22:55:23 +00:00
if ( ! turn_around & & v - > load_unload_time_rem % _settings_game . pf . path_backoff_interval ! = 0 & & v - > u . rail . force_proceed = = 0 ) return ;
2008-08-02 22:54:38 +00:00
if ( ! TryPathReserve ( v ) ) {
/* Still stuck. */
if ( turn_around ) ReverseTrainDirection ( v ) ;
if ( HasBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) & & v - > load_unload_time_rem > 2 * _settings_game . pf . wait_for_pbs_path * DAY_TICKS ) {
/* Show message to player. */
2008-09-30 20:39:50 +00:00
if ( _settings_client . gui . lost_train_warn & & v - > owner = = _local_company ) {
2009-01-04 17:42:46 +00:00
SetDParam ( 0 , v - > index ) ;
2008-08-02 22:54:38 +00:00
AddNewsItem (
STR_TRAIN_IS_STUCK ,
NS_ADVICE ,
v - > index ,
0 ) ;
}
v - > load_unload_time_rem = 0 ;
}
/* Exit if force proceed not pressed, else reset stuck flag anyway. */
if ( v - > u . rail . force_proceed = = 0 ) return ;
ClrBit ( v - > u . rail . flags , VRF_TRAIN_STUCK ) ;
v - > load_unload_time_rem = 0 ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
}
}
2009-01-11 15:39:49 +00:00
if ( v - > current_order . IsType ( OT_LEAVESTATION ) ) {
v - > current_order . Free ( ) ;
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
return ;
}
2007-02-25 09:27:03 +00:00
int j = UpdateTrainSpeed ( v ) ;
2008-01-27 20:20:53 +00:00
/* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
if ( v - > cur_speed = = 0 & & v - > u . rail . last_speed = = 0 & & v - > vehstatus & VS_STOPPED ) {
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
}
2008-10-03 12:55:39 +00:00
int adv_spd = ( v - > direction & 1 ) ? 192 : 256 ;
if ( j < adv_spd ) {
2007-04-04 03:21:14 +00:00
/* if the vehicle has speed 0, update the last_speed field. */
2008-10-03 12:55:39 +00:00
if ( v - > cur_speed = = 0 ) SetLastSpeed ( v , v - > cur_speed ) ;
2004-08-09 17:04:08 +00:00
} else {
TrainCheckIfLineEnds ( v ) ;
2008-10-03 12:55:39 +00:00
/* Loop until the train has finished moving. */
2004-08-09 17:04:08 +00:00
do {
2008-10-03 12:55:39 +00:00
j - = adv_spd ;
2008-03-19 20:42:05 +00:00
TrainController ( v , NULL , true ) ;
2008-10-22 23:06:36 +00:00
/* Don't continue to move if the train crashed. */
if ( CheckTrainCollision ( v ) ) break ;
2008-10-03 12:55:39 +00:00
/* 192 spd used for going straight, 256 for going diagonally. */
adv_spd = ( v - > direction & 1 ) ? 192 : 256 ;
} while ( j > = adv_spd ) ;
SetLastSpeed ( v , v - > cur_speed ) ;
2004-08-09 17:04:08 +00:00
}
2009-01-09 22:48:57 +00:00
if ( v - > progress = = 0 ) v - > progress = j ; // Save unused spd for next time, if TrainController didn't set progress
2004-08-09 17:04:08 +00:00
}
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 ) ;
2008-02-20 20:56:54 +00:00
byte cost_factor = GetVehicleProperty ( v , 0x0D , rvi - > running_cost ) ;
2007-08-29 21:27:16 +00:00
if ( cost_factor = = 0 ) continue ;
2008-05-13 20:39:57 +00:00
/* Halve running cost for multiheaded parts */
if ( IsMultiheaded ( v ) ) cost_factor / = 2 ;
2008-02-21 19:09:10 +00:00
cost + = cost_factor * GetPriceByIndex ( rvi - > running_cost_class ) ;
2007-08-29 21:27:16 +00:00
} 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 ) ) {
2008-02-13 19:24:40 +00:00
if ( ! ( this - > vehstatus & VS_STOPPED ) ) this - > running_ticks + + ;
2007-07-01 19:24:54 +00:00
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-10-08 20:16:25 +00:00
/* Delete flooded standalone wagon chain */
2008-12-26 20:45:02 +00:00
if ( + + this - > u . rail . crash_anim_pos > = 4400 ) delete this ;
2004-08-09 17:04:08 +00:00
}
}
static void CheckIfTrainNeedsService ( Vehicle * v )
{
2008-04-07 12:36:50 +00:00
static const uint MAX_ACCEPTABLE_DEPOT_DIST = 16 ;
2008-05-29 15:13:28 +00:00
if ( _settings_game . vehicle . servint_trains = = 0 | | ! v - > NeedsAutomaticServicing ( ) ) return ;
2007-08-31 17:13:39 +00:00
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. */
2008-10-14 18:38:51 +00:00
if ( tfdd . best_length = = UINT_MAX | | tfdd . best_length > MAX_ACCEPTABLE_DEPOT_DIST ) {
2008-04-05 23:36:54 +00:00
if ( v - > current_order . IsType ( 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 ? */
2008-04-05 23:36:54 +00:00
v - > current_order . MakeDummy ( ) ;
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
2004-08-09 17:04:08 +00:00
}
return ;
}
2008-04-07 12:36:50 +00:00
const Depot * depot = GetDepotByTile ( tfdd . tile ) ;
2004-08-09 17:04:08 +00:00
2008-04-05 23:36:54 +00:00
if ( v - > current_order . IsType ( OT_GOTO_DEPOT ) & &
2008-04-06 07:48:51 +00:00
v - > current_order . GetDestination ( ) ! = depot - > index & &
2007-11-25 15:35:25 +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
2008-04-07 19:18:56 +00:00
v - > current_order . MakeGoToDepot ( depot - > index , ODTFB_SERVICE ) ;
2004-09-23 21:20:38 +00:00
v - > dest_tile = tfdd . tile ;
2008-01-18 13:02:47 +00:00
InvalidateWindowWidget ( WC_VEHICLE_VIEW , v - > index , VVW_WIDGET_START_STOP_VEH ) ;
2004-08-09 17:04:08 +00:00
}
2008-02-01 22:02:14 +00:00
void Train : : OnNewDay ( )
2004-08-09 17:04:08 +00:00
{
2008-02-01 22:02:14 +00:00
if ( ( + + this - > day_counter & 7 ) = = 0 ) DecreaseVehicleValue ( this ) ;
2004-08-09 17:04:08 +00:00
2008-02-01 22:02:14 +00:00
if ( IsFrontEngine ( this ) ) {
CheckVehicleBreakdown ( this ) ;
AgeVehicle ( this ) ;
2004-09-10 19:02:27 +00:00
2008-02-01 22:02:14 +00:00
CheckIfTrainNeedsService ( this ) ;
2004-09-10 19:02:27 +00:00
2008-02-01 22:02:14 +00:00
CheckOrders ( this ) ;
2004-09-10 19:02:27 +00:00
2004-08-09 17:04:08 +00:00
/* update destination */
2008-04-05 23:36:54 +00:00
if ( this - > current_order . IsType ( OT_GOTO_STATION ) ) {
2008-04-06 07:48:51 +00:00
TileIndex tile = GetStation ( this - > current_order . GetDestination ( ) ) - > train_tile ;
2008-12-26 18:01:15 +00:00
if ( tile ! = INVALID_TILE ) this - > dest_tile = tile ;
2005-11-14 19:48:04 +00:00
}
2004-08-09 17:04:08 +00:00
2008-02-13 19:24:40 +00:00
if ( this - > running_ticks ! = 0 ) {
2004-08-09 17:04:08 +00:00
/* running costs */
2008-02-13 19:24:40 +00:00
CommandCost cost ( EXPENSES_TRAIN_RUN , this - > GetRunningCost ( ) * this - > running_ticks / ( 364 * DAY_TICKS ) ) ;
2004-08-09 17:04:08 +00:00
2008-02-13 19:24:40 +00:00
this - > profit_this_year - = cost . GetCost ( ) ;
this - > running_ticks = 0 ;
2004-08-09 17:04:08 +00:00
2008-09-30 20:39:50 +00:00
SubtractMoneyFromCompanyFract ( this - > owner , cost ) ;
2004-08-09 17:04:08 +00:00
2008-02-01 22:02:14 +00:00
InvalidateWindow ( WC_VEHICLE_DETAILS , this - > index ) ;
2005-01-24 22:24:47 +00:00
InvalidateWindowClasses ( WC_TRAINS_LIST ) ;
2004-08-09 17:04:08 +00:00
}
2008-02-01 22:02:14 +00:00
} else if ( IsTrainEngine ( this ) ) {
2007-06-22 22:42:18 +00:00
/* Also age engines that aren't front engines */
2008-02-01 22:02:14 +00:00
AgeVehicle ( this ) ;
2004-08-09 17:04:08 +00:00
}
}
2007-03-07 11:47:46 +00:00
void InitializeTrains ( )
2004-08-09 17:04:08 +00:00
{
_age_cargo_skip_counter = 1 ;
}