2010-03-06 13:41:18 +01:00
/*
* This file is part of OpenTTD .
* OpenTTD is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation , version 2.
* OpenTTD is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
/** @file ground_vehicle.hpp Base class and functions for all vehicles that move through ground. */
# ifndef GROUND_VEHICLE_HPP
# define GROUND_VEHICLE_HPP
# include "vehicle_base.h"
2011-01-26 18:31:07 +01:00
# include "vehicle_gui.h"
2010-03-06 13:52:44 +01:00
# include "landscape.h"
2011-01-26 18:31:07 +01:00
# include "window_func.h"
2024-03-31 20:37:16 +02:00
2012-01-03 21:26:05 +01:00
# include "widgets/vehicle_widget.h"
2010-03-06 13:41:18 +01:00
2010-03-06 13:42:53 +01:00
/** What is the status of our acceleration? */
enum AccelStatus {
AS_ACCEL , ///< We want to go faster, if possible of course.
2011-12-19 18:48:04 +01:00
AS_BRAKE , ///< We want to stop.
2010-03-06 13:42:53 +01:00
} ;
/**
2010-12-14 22:31:45 +01:00
* Cached , frequently calculated values .
2010-03-06 13:42:53 +01:00
* All of these values except cached_slope_resistance are set only for the first part of a vehicle .
*/
2010-12-14 22:28:45 +01:00
struct GroundVehicleCache {
/* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
2023-05-08 19:01:06 +02:00
uint32_t cached_weight ; ///< Total weight of the consist (valid only for the first engine).
uint32_t cached_slope_resistance ; ///< Resistance caused by weight when this vehicle part is at a slope.
uint32_t cached_max_te ; ///< Maximum tractive effort of consist (valid only for the first engine).
uint16_t cached_axle_resistance ; ///< Resistance caused by the axles of the vehicle (valid only for the first engine).
2010-03-06 13:42:53 +01:00
2010-12-14 22:28:45 +01:00
/* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
2023-05-08 19:01:06 +02:00
uint16_t cached_max_track_speed ; ///< Maximum consist speed (in internal units) limited by track type (valid only for the first engine).
uint32_t cached_power ; ///< Total power of the consist (valid only for the first engine).
uint32_t cached_air_drag ; ///< Air drag coefficient of the vehicle (valid only for the first engine).
2010-12-14 22:33:53 +01:00
/* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
2023-05-08 19:01:06 +02:00
uint16_t cached_total_length ; ///< Length of the whole vehicle (valid only for the first engine).
2010-12-14 22:33:53 +01:00
EngineID first_engine ; ///< Cached EngineID of the front vehicle. INVALID_ENGINE for the front vehicle itself.
2023-05-08 19:01:06 +02:00
uint8_t cached_veh_length ; ///< Length of this vehicle in units of 1/VEHICLE_LENGTH of normal length. It is cached because this can be set by a callback.
2011-01-26 18:31:07 +01:00
/* Cached UI information. */
2023-05-08 19:01:06 +02:00
uint16_t last_speed ; ///< The last speed we did display, so we only have to redraw when this changes.
2024-04-16 21:57:12 +02:00
auto operator < = > ( const GroundVehicleCache & ) const = default ;
2010-03-06 13:42:53 +01:00
} ;
2010-03-06 13:50:55 +01:00
/** Ground vehicle flags. */
enum GroundVehicleFlags {
2011-12-19 18:48:04 +01:00
GVF_GOINGUP_BIT = 0 , ///< Vehicle is currently going uphill. (Cached track information for acceleration)
GVF_GOINGDOWN_BIT = 1 , ///< Vehicle is currently going downhill. (Cached track information for acceleration)
2011-05-18 14:19:58 +02:00
GVF_SUPPRESS_IMPLICIT_ORDERS = 2 , ///< Disable insertion and removal of automatic orders until the vehicle completes the real order.
2010-03-06 13:50:55 +01:00
} ;
2010-03-06 13:41:18 +01:00
/**
* Base class for all vehicles that move through ground .
2010-03-06 13:45:47 +01:00
*
* Child classes must define all of the following functions .
* These functions are not defined as pure virtual functions at this class to improve performance .
*
2023-05-08 19:01:06 +02:00
* virtual uint16_t GetPower ( ) const = 0 ;
* virtual uint16_t GetPoweredPartPower ( const T * head ) const = 0 ;
* virtual uint16_t GetWeight ( ) const = 0 ;
2024-03-16 23:59:32 +01:00
* virtual uint8_t GetTractiveEffort ( ) const = 0 ;
* virtual uint8_t GetAirDrag ( ) const = 0 ;
* virtual uint8_t GetAirDragArea ( ) const = 0 ;
2010-03-06 13:45:47 +01:00
* virtual AccelStatus GetAccelerationStatus ( ) const = 0 ;
2023-05-08 19:01:06 +02:00
* virtual uint16_t GetCurrentSpeed ( ) const = 0 ;
* virtual uint32_t GetRollingFriction ( ) const = 0 ;
2010-03-06 13:45:47 +01:00
* virtual int GetAccelerationType ( ) const = 0 ;
2023-05-08 19:01:06 +02:00
* virtual int32_t GetSlopeSteepness ( ) const = 0 ;
2010-11-06 13:40:38 +01:00
* virtual int GetDisplayMaxSpeed ( ) const = 0 ;
2023-05-08 19:01:06 +02:00
* virtual uint16_t GetMaxTrackSpeed ( ) const = 0 ;
2010-03-06 13:52:44 +01:00
* virtual bool TileMayHaveSlopedTrack ( ) const = 0 ;
2010-03-06 13:41:18 +01:00
*/
template < class T , VehicleType Type >
struct GroundVehicle : public SpecializedVehicle < T , Type > {
2010-12-14 22:28:45 +01:00
GroundVehicleCache gcache ; ///< Cache of often calculated values.
2023-05-08 19:01:06 +02:00
uint16_t gv_flags ; ///< @see GroundVehicleFlags.
2010-03-06 13:41:18 +01:00
2011-01-21 15:43:38 +01:00
typedef GroundVehicle < T , Type > GroundVehicleBase ; ///< Our type
2010-03-06 13:41:18 +01:00
/**
* The constructor at SpecializedVehicle must be called .
*/
GroundVehicle ( ) : SpecializedVehicle < T , Type > ( ) { }
2010-03-06 13:44:30 +01:00
void PowerChanged ( ) ;
void CargoChanged ( ) ;
int GetAcceleration ( ) const ;
2019-03-03 23:25:13 +01:00
bool IsChainInDepot ( ) const override ;
2010-03-06 13:50:55 +01:00
2011-01-21 17:20:55 +01:00
/**
* Common code executed for crashed ground vehicles
* @ param flooded was this vehicle flooded ?
* @ return number of victims
*/
2019-03-03 23:25:13 +01:00
uint Crash ( bool flooded ) override
2011-01-21 17:20:55 +01:00
{
/* Crashed vehicles aren't going up or down */
2019-04-10 23:07:06 +02:00
for ( T * v = T : : From ( this ) ; v ! = nullptr ; v = v - > Next ( ) ) {
2011-01-21 17:20:55 +01:00
ClrBit ( v - > gv_flags , GVF_GOINGUP_BIT ) ;
ClrBit ( v - > gv_flags , GVF_GOINGDOWN_BIT ) ;
}
return this - > Vehicle : : Crash ( flooded ) ;
}
2010-03-06 17:15:50 +01:00
/**
2010-03-06 13:50:55 +01:00
* Calculates the total slope resistance for this vehicle .
* @ return Slope resistance .
*/
2023-05-08 19:01:06 +02:00
inline int64_t GetSlopeResistance ( ) const
2010-03-06 13:50:55 +01:00
{
2023-05-08 19:01:06 +02:00
int64_t incl = 0 ;
2010-03-06 13:50:55 +01:00
2019-04-10 23:07:06 +02:00
for ( const T * u = T : : From ( this ) ; u ! = nullptr ; u = u - > Next ( ) ) {
2010-03-06 13:50:55 +01:00
if ( HasBit ( u - > gv_flags , GVF_GOINGUP_BIT ) ) {
2010-12-14 22:28:45 +01:00
incl + = u - > gcache . cached_slope_resistance ;
2010-03-06 13:50:55 +01:00
} else if ( HasBit ( u - > gv_flags , GVF_GOINGDOWN_BIT ) ) {
2010-12-14 22:28:45 +01:00
incl - = u - > gcache . cached_slope_resistance ;
2010-03-06 13:50:55 +01:00
}
}
return incl ;
}
2010-03-06 13:52:44 +01:00
2011-01-21 17:13:54 +01:00
/**
* Updates vehicle ' s Z position and inclination .
* Used when the vehicle entered given tile .
* @ pre The vehicle has to be at ( or near to ) a border of the tile ,
* directed towards tile centre
*/
2011-12-20 18:57:56 +01:00
inline void UpdateZPositionAndInclination ( )
2011-01-21 17:13:54 +01:00
{
2023-03-12 18:14:44 +01:00
this - > z_pos = GetSlopePixelZ ( this - > x_pos , this - > y_pos , true ) ;
2011-01-21 17:13:54 +01:00
ClrBit ( this - > gv_flags , GVF_GOINGUP_BIT ) ;
ClrBit ( this - > gv_flags , GVF_GOINGDOWN_BIT ) ;
if ( T : : From ( this ) - > TileMayHaveSlopedTrack ( ) ) {
/* 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 . */
2023-03-12 18:14:44 +01:00
int middle_z = GetSlopePixelZ ( ( this - > x_pos & ~ TILE_UNIT_MASK ) | ( TILE_SIZE / 2 ) , ( this - > y_pos & ~ TILE_UNIT_MASK ) | ( TILE_SIZE / 2 ) , true ) ;
2011-01-21 17:13:54 +01:00
if ( middle_z ! = this - > z_pos ) {
SetBit ( this - > gv_flags , ( middle_z > this - > z_pos ) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT ) ;
}
}
}
/**
* Updates vehicle ' s Z position .
* Inclination can ' t change in the middle of a tile .
2011-01-21 18:35:17 +01:00
* The faster code is used for trains and road vehicles unless they are
* reversing on a sloped tile .
2011-01-21 17:13:54 +01:00
*/
2011-12-20 18:57:56 +01:00
inline void UpdateZPosition ( )
2011-01-21 17:13:54 +01:00
{
2011-01-21 18:35:17 +01:00
#if 0
/* The following code does this: */
if ( HasBit ( this - > gv_flags , GVF_GOINGUP_BIT ) ) {
switch ( this - > direction ) {
case DIR_NE :
this - > z_pos + = ( this - > x_pos & 1 ) ^ 1 ; break ;
2023-02-25 23:58:46 +01:00
case DIR_SW :
this - > z_pos + = ( this - > x_pos & 1 ) ; break ;
2011-01-21 18:35:17 +01:00
case DIR_NW :
this - > z_pos + = ( this - > y_pos & 1 ) ^ 1 ; break ;
2023-02-25 23:58:46 +01:00
case DIR_SE :
this - > z_pos + = ( this - > y_pos & 1 ) ; break ;
2011-01-21 18:35:17 +01:00
default : break ;
}
} else if ( HasBit ( this - > gv_flags , GVF_GOINGDOWN_BIT ) ) {
switch ( this - > direction ) {
case DIR_NE :
this - > z_pos - = ( this - > x_pos & 1 ) ^ 1 ; break ;
2023-02-25 23:58:46 +01:00
case DIR_SW :
this - > z_pos - = ( this - > x_pos & 1 ) ; break ;
2011-01-21 18:35:17 +01:00
case DIR_NW :
this - > z_pos - = ( this - > y_pos & 1 ) ^ 1 ; break ;
2023-02-25 23:58:46 +01:00
case DIR_SE :
this - > z_pos - = ( this - > y_pos & 1 ) ; break ;
2011-01-21 18:35:17 +01:00
default : break ;
}
}
/* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
* code is full of conditional jumps . */
# endif
/* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
* Furthermore , if this function is called once every time the vehicle ' s position changes ,
* we know the Z position changes by + / - 1 at certain moments - when x_pos , y_pos is odd / even ,
* depending on orientation of the slope and vehicle ' s direction */
2011-01-21 17:51:56 +01:00
if ( HasBit ( this - > gv_flags , GVF_GOINGUP_BIT ) | | HasBit ( this - > gv_flags , GVF_GOINGDOWN_BIT ) ) {
2011-11-04 11:18:13 +01:00
if ( T : : From ( this ) - > HasToUseGetSlopePixelZ ( ) ) {
/* In some cases, we have to use GetSlopePixelZ() */
2023-03-12 18:14:44 +01:00
this - > z_pos = GetSlopePixelZ ( this - > x_pos , this - > y_pos , true ) ;
2011-01-21 18:35:17 +01:00
return ;
}
/* DirToDiagDir() is a simple right shift */
DiagDirection dir = DirToDiagDir ( this - > direction ) ;
/* Read variables, so the compiler knows the access doesn't trap */
2023-05-08 19:01:06 +02:00
int8_t x_pos = this - > x_pos ;
int8_t y_pos = this - > y_pos ;
2011-01-21 18:35:17 +01:00
/* DiagDirToAxis() is a simple mask */
2023-05-08 19:01:06 +02:00
int8_t d = DiagDirToAxis ( dir ) = = AXIS_X ? x_pos : y_pos ;
2011-01-21 18:35:17 +01:00
/* We need only the least significant bit */
d & = 1 ;
2023-05-08 19:01:06 +02:00
d ^ = ( int8_t ) ( dir = = DIAGDIR_NW | | dir = = DIAGDIR_NE ) ;
2011-01-21 18:35:17 +01:00
/* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
* GVF_GOINGUP_BIT is used because it ' s bit 0 , so simple AND can be used ,
* without any shift */
this - > z_pos + = HasBit ( this - > gv_flags , GVF_GOINGUP_BIT ) ? d : - d ;
2011-01-21 17:13:54 +01:00
}
2011-01-21 18:35:17 +01:00
2023-03-12 18:14:44 +01:00
assert ( this - > z_pos = = GetSlopePixelZ ( this - > x_pos , this - > y_pos , true ) ) ;
2011-01-21 17:13:54 +01:00
}
2010-03-06 13:52:44 +01:00
/**
* Checks if the vehicle is in a slope and sets the required flags in that case .
* @ param new_tile True if the vehicle reached a new tile .
2012-01-02 14:44:37 +01:00
* @ param update_delta Indicates to also update the delta .
2010-03-06 13:52:44 +01:00
* @ return Old height of the vehicle .
*/
2014-09-21 14:44:38 +02:00
inline int UpdateInclination ( bool new_tile , bool update_delta )
2010-03-06 13:52:44 +01:00
{
2014-09-21 14:44:38 +02:00
int old_z = this - > z_pos ;
2010-03-06 13:52:44 +01:00
if ( new_tile ) {
2011-01-21 17:13:54 +01:00
this - > UpdateZPositionAndInclination ( ) ;
2011-01-16 19:18:45 +01:00
} else {
2011-01-21 17:13:54 +01:00
this - > UpdateZPosition ( ) ;
2010-03-06 13:52:44 +01:00
}
2012-01-02 14:44:37 +01:00
this - > UpdateViewport ( true , update_delta ) ;
2010-03-06 13:52:44 +01:00
return old_z ;
}
2011-01-19 19:42:54 +01:00
2011-01-19 21:04:09 +01:00
/**
* Set front engine state .
*/
2011-12-20 18:57:56 +01:00
inline void SetFrontEngine ( ) { SetBit ( this - > subtype , GVSF_FRONT ) ; }
2011-01-19 21:04:09 +01:00
/**
* Remove the front engine state .
*/
2011-12-20 18:57:56 +01:00
inline void ClearFrontEngine ( ) { ClrBit ( this - > subtype , GVSF_FRONT ) ; }
2011-01-19 21:04:09 +01:00
/**
* Set a vehicle to be an articulated part .
*/
2011-12-20 18:57:56 +01:00
inline void SetArticulatedPart ( ) { SetBit ( this - > subtype , GVSF_ARTICULATED_PART ) ; }
2011-01-19 21:04:09 +01:00
/**
* Clear a vehicle from being an articulated part .
*/
2011-12-20 18:57:56 +01:00
inline void ClearArticulatedPart ( ) { ClrBit ( this - > subtype , GVSF_ARTICULATED_PART ) ; }
2011-01-19 21:04:09 +01:00
/**
* Set a vehicle to be a wagon .
*/
2011-12-20 18:57:56 +01:00
inline void SetWagon ( ) { SetBit ( this - > subtype , GVSF_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Clear wagon property .
*/
2011-12-20 18:57:56 +01:00
inline void ClearWagon ( ) { ClrBit ( this - > subtype , GVSF_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Set engine status .
*/
2011-12-20 18:57:56 +01:00
inline void SetEngine ( ) { SetBit ( this - > subtype , GVSF_ENGINE ) ; }
2011-01-19 21:04:09 +01:00
/**
* Clear engine status .
*/
2011-12-20 18:57:56 +01:00
inline void ClearEngine ( ) { ClrBit ( this - > subtype , GVSF_ENGINE ) ; }
2011-01-19 21:04:09 +01:00
/**
* Set a vehicle as a free wagon .
*/
2011-12-20 18:57:56 +01:00
inline void SetFreeWagon ( ) { SetBit ( this - > subtype , GVSF_FREE_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Clear a vehicle from being a free wagon .
*/
2011-12-20 18:57:56 +01:00
inline void ClearFreeWagon ( ) { ClrBit ( this - > subtype , GVSF_FREE_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Set a vehicle as a multiheaded engine .
*/
2011-12-20 18:57:56 +01:00
inline void SetMultiheaded ( ) { SetBit ( this - > subtype , GVSF_MULTIHEADED ) ; }
2011-01-19 21:04:09 +01:00
/**
* Clear multiheaded engine property .
*/
2011-12-20 18:57:56 +01:00
inline void ClearMultiheaded ( ) { ClrBit ( this - > subtype , GVSF_MULTIHEADED ) ; }
2011-01-19 21:04:09 +01:00
/**
* Check if the vehicle is a free wagon ( got no engine in front of it ) .
* @ return Returns true if the vehicle is a free wagon .
*/
2011-12-20 18:57:56 +01:00
inline bool IsFreeWagon ( ) const { return HasBit ( this - > subtype , GVSF_FREE_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Check if a vehicle is an engine ( can be first in a consist ) .
* @ return Returns true if vehicle is an engine .
*/
2011-12-20 18:57:56 +01:00
inline bool IsEngine ( ) const { return HasBit ( this - > subtype , GVSF_ENGINE ) ; }
2011-01-19 21:04:09 +01:00
/**
* Check if a vehicle is a wagon .
* @ return Returns true if vehicle is a wagon .
*/
2011-12-20 18:57:56 +01:00
inline bool IsWagon ( ) const { return HasBit ( this - > subtype , GVSF_WAGON ) ; }
2011-01-19 21:04:09 +01:00
/**
* Check if the vehicle is a multiheaded engine .
* @ return Returns true if the vehicle is a multiheaded engine .
*/
2011-12-20 18:57:56 +01:00
inline bool IsMultiheaded ( ) const { return HasBit ( this - > subtype , GVSF_MULTIHEADED ) ; }
2011-01-19 21:04:09 +01:00
/**
* Tell if we are dealing with the rear end of a multiheaded engine .
* @ return True if the engine is the rear part of a dualheaded engine .
*/
2011-12-20 18:57:56 +01:00
inline bool IsRearDualheaded ( ) const { return this - > IsMultiheaded ( ) & & ! this - > IsEngine ( ) ; }
2011-01-19 21:04:09 +01:00
2011-01-26 18:31:07 +01:00
/**
* Update the GUI variant of the current speed of the vehicle .
* Also mark the widget dirty when that is needed , i . e . when
* the speed of this vehicle has changed .
*/
2011-12-20 18:57:56 +01:00
inline void SetLastSpeed ( )
2011-01-26 18:31:07 +01:00
{
if ( this - > cur_speed ! = this - > gcache . last_speed ) {
2011-12-16 17:58:55 +01:00
SetWindowWidgetDirty ( WC_VEHICLE_VIEW , this - > index , WID_VV_START_STOP ) ;
2011-01-26 18:31:07 +01:00
this - > gcache . last_speed = this - > cur_speed ;
}
}
2011-01-26 18:34:07 +01:00
protected :
/**
* Update the speed of the vehicle .
*
* It updates the cur_speed and subspeed variables depending on the state
* of the vehicle ; in this case the current acceleration , minimum and
* maximum speeds of the vehicle . It returns the distance that that the
* vehicle can drive this tick . # Vehicle : : GetAdvanceDistance ( ) determines
* the distance to drive before moving a step on the map .
* @ param accel The acceleration we would like to give this vehicle .
* @ param min_speed The minimum speed here , in vehicle specific units .
* @ param max_speed The maximum speed here , in vehicle specific units .
* @ return Distance to drive .
*/
2011-12-20 18:57:56 +01:00
inline uint DoUpdateSpeed ( uint accel , int min_speed , int max_speed )
2011-01-26 18:34:07 +01:00
{
uint spd = this - > subspeed + accel ;
2024-03-16 23:59:32 +01:00
this - > subspeed = ( uint8_t ) spd ;
2011-01-26 18:34:07 +01:00
/* When we are going faster than the maximum speed, reduce the speed
* somewhat gradually . But never lower than the maximum speed . */
int tempmax = max_speed ;
if ( this - > cur_speed > max_speed ) {
2021-01-08 11:16:18 +01:00
tempmax = std : : max ( this - > cur_speed - ( this - > cur_speed / 10 ) - 1 , max_speed ) ;
2011-01-26 18:34:07 +01:00
}
2011-01-26 18:37:12 +01:00
/* Enforce a maximum and minimum speed. Normally we would use something like
* Clamp for this , but in this case min_speed might be below the maximum speed
* threshold for some reason . That makes acceleration fail and assertions
* happen in Clamp . So make it explicit that min_speed overrules the maximum
* speed by explicit ordering of min and max . */
2021-01-08 11:16:18 +01:00
this - > cur_speed = spd = std : : max ( std : : min ( this - > cur_speed + ( ( int ) spd > > 8 ) , tempmax ) , min_speed ) ;
2011-01-26 18:34:07 +01:00
int scaled_spd = this - > GetAdvanceSpeed ( spd ) ;
scaled_spd + = this - > progress ;
this - > progress = 0 ; // set later in *Handler or *Controller
return scaled_spd ;
}
2010-03-06 13:41:18 +01:00
} ;
# endif /* GROUND_VEHICLE_HPP */