OpenTTD Source  20241124-master-g9399a92a4f
ground_vehicle.hpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
10 #ifndef GROUND_VEHICLE_HPP
11 #define GROUND_VEHICLE_HPP
12 
13 #include "vehicle_base.h"
14 #include "vehicle_gui.h"
15 #include "landscape.h"
16 #include "window_func.h"
17 
18 #include "widgets/vehicle_widget.h"
19 
24 };
25 
31  /* Cached acceleration values, recalculated when the cargo on a vehicle changes (in addition to the conditions below) */
32  uint32_t cached_weight;
34  uint32_t cached_max_te;
36 
37  /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
39  uint32_t cached_power;
40  uint32_t cached_air_drag;
41 
42  /* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
46 
47  /* Cached UI information. */
48  uint16_t last_speed;
49 
50  auto operator<=>(const GroundVehicleCache &) const = default;
51 };
52 
58 };
59 
81 template <class T, VehicleType Type>
82 struct GroundVehicle : public SpecializedVehicle<T, Type> {
84  uint16_t gv_flags;
85 
87 
92 
93  void PowerChanged();
94  void CargoChanged();
95  int GetAcceleration() const;
96  bool IsChainInDepot() const override;
97 
103  uint Crash(bool flooded) override
104  {
105  /* Crashed vehicles aren't going up or down */
106  for (T *v = T::From(this); v != nullptr; v = v->Next()) {
107  ClrBit(v->gv_flags, GVF_GOINGUP_BIT);
108  ClrBit(v->gv_flags, GVF_GOINGDOWN_BIT);
109  }
110  return this->Vehicle::Crash(flooded);
111  }
112 
117  inline int64_t GetSlopeResistance() const
118  {
119  int64_t incl = 0;
120 
121  for (const T *u = T::From(this); u != nullptr; u = u->Next()) {
122  if (HasBit(u->gv_flags, GVF_GOINGUP_BIT)) {
123  incl += u->gcache.cached_slope_resistance;
124  } else if (HasBit(u->gv_flags, GVF_GOINGDOWN_BIT)) {
125  incl -= u->gcache.cached_slope_resistance;
126  }
127  }
128 
129  return incl;
130  }
131 
139  {
140  this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true);
141  ClrBit(this->gv_flags, GVF_GOINGUP_BIT);
142  ClrBit(this->gv_flags, GVF_GOINGDOWN_BIT);
143 
144  if (T::From(this)->TileMayHaveSlopedTrack()) {
145  /* To check whether the current tile is sloped, and in which
146  * direction it is sloped, we get the 'z' at the center of
147  * the tile (middle_z) and the edge of the tile (old_z),
148  * which we then can compare. */
149  int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), true);
150 
151  if (middle_z != this->z_pos) {
152  SetBit(this->gv_flags, (middle_z > this->z_pos) ? GVF_GOINGUP_BIT : GVF_GOINGDOWN_BIT);
153  }
154  }
155  }
156 
163  inline void UpdateZPosition()
164  {
165 #if 0
166  /* The following code does this: */
167 
168  if (HasBit(this->gv_flags, GVF_GOINGUP_BIT)) {
169  switch (this->direction) {
170  case DIR_NE:
171  this->z_pos += (this->x_pos & 1) ^ 1; break;
172  case DIR_SW:
173  this->z_pos += (this->x_pos & 1); break;
174  case DIR_NW:
175  this->z_pos += (this->y_pos & 1) ^ 1; break;
176  case DIR_SE:
177  this->z_pos += (this->y_pos & 1); break;
178  default: break;
179  }
180  } else if (HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
181  switch (this->direction) {
182  case DIR_NE:
183  this->z_pos -= (this->x_pos & 1) ^ 1; break;
184  case DIR_SW:
185  this->z_pos -= (this->x_pos & 1); break;
186  case DIR_NW:
187  this->z_pos -= (this->y_pos & 1) ^ 1; break;
188  case DIR_SE:
189  this->z_pos -= (this->y_pos & 1); break;
190  default: break;
191  }
192  }
193 
194  /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
195  * code is full of conditional jumps. */
196 #endif
197 
198  /* Vehicle's Z position can change only if it has GVF_GOINGUP_BIT or GVF_GOINGDOWN_BIT set.
199  * Furthermore, if this function is called once every time the vehicle's position changes,
200  * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
201  * depending on orientation of the slope and vehicle's direction */
202 
203  if (HasBit(this->gv_flags, GVF_GOINGUP_BIT) || HasBit(this->gv_flags, GVF_GOINGDOWN_BIT)) {
204  if (T::From(this)->HasToUseGetSlopePixelZ()) {
205  /* In some cases, we have to use GetSlopePixelZ() */
206  this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true);
207  return;
208  }
209  /* DirToDiagDir() is a simple right shift */
210  DiagDirection dir = DirToDiagDir(this->direction);
211  /* Read variables, so the compiler knows the access doesn't trap */
212  int8_t x_pos = this->x_pos;
213  int8_t y_pos = this->y_pos;
214  /* DiagDirToAxis() is a simple mask */
215  int8_t d = DiagDirToAxis(dir) == AXIS_X ? x_pos : y_pos;
216  /* We need only the least significant bit */
217  d &= 1;
218  d ^= (int8_t)(dir == DIAGDIR_NW || dir == DIAGDIR_NE);
219  /* Subtraction instead of addition because we are testing for GVF_GOINGUP_BIT.
220  * GVF_GOINGUP_BIT is used because it's bit 0, so simple AND can be used,
221  * without any shift */
222  this->z_pos += HasBit(this->gv_flags, GVF_GOINGUP_BIT) ? d : -d;
223  }
224 
225  assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos, true));
226  }
227 
234  inline int UpdateInclination(bool new_tile, bool update_delta)
235  {
236  int old_z = this->z_pos;
237 
238  if (new_tile) {
240  } else {
241  this->UpdateZPosition();
242  }
243 
244  this->UpdateViewport(true, update_delta);
245  return old_z;
246  }
247 
251  inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
252 
256  inline void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
257 
262 
267 
271  inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
272 
276  inline void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
277 
281  inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
282 
286  inline void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
287 
291  inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
292 
296  inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
297 
301  inline void SetMultiheaded() { SetBit(this->subtype, GVSF_MULTIHEADED); }
302 
306  inline void ClearMultiheaded() { ClrBit(this->subtype, GVSF_MULTIHEADED); }
307 
312  inline bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
313 
318  inline bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
319 
324  inline bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
325 
330  inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
331 
336  inline bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
337 
343  inline void SetLastSpeed()
344  {
345  if (this->cur_speed != this->gcache.last_speed) {
347  this->gcache.last_speed = this->cur_speed;
348  }
349  }
350 
351 protected:
365  inline uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
366  {
367  uint spd = this->subspeed + accel;
368  this->subspeed = (uint8_t)spd;
369 
370  /* When we are going faster than the maximum speed, reduce the speed
371  * somewhat gradually. But never lower than the maximum speed. */
372  int tempmax = max_speed;
373  if (this->cur_speed > max_speed) {
374  tempmax = std::max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
375  }
376 
377  /* Enforce a maximum and minimum speed. Normally we would use something like
378  * Clamp for this, but in this case min_speed might be below the maximum speed
379  * threshold for some reason. That makes acceleration fail and assertions
380  * happen in Clamp. So make it explicit that min_speed overrules the maximum
381  * speed by explicit ordering of min and max. */
382  this->cur_speed = spd = std::max(std::min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
383 
384  int scaled_spd = this->GetAdvanceSpeed(spd);
385 
386  scaled_spd += this->progress;
387  this->progress = 0; // set later in *Handler or *Controller
388  return scaled_spd;
389  }
390 };
391 
392 #endif /* GROUND_VEHICLE_HPP */
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
@ DIR_SW
Southwest.
@ DIR_NW
Northwest.
@ DIR_SE
Southeast.
@ DIR_NE
Northeast.
@ AXIS_X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
uint16_t EngineID
Unique identification number of an engine.
Definition: engine_type.h:21
AccelStatus
What is the status of our acceleration?
@ AS_BRAKE
We want to stop.
@ AS_ACCEL
We want to go faster, if possible of course.
GroundVehicleFlags
Ground vehicle flags.
@ GVF_SUPPRESS_IMPLICIT_ORDERS
Disable insertion and removal of automatic orders until the vehicle completes the real order.
@ GVF_GOINGDOWN_BIT
Vehicle is currently going downhill. (Cached track information for acceleration)
@ GVF_GOINGUP_BIT
Vehicle is currently going uphill. (Cached track information for acceleration)
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition: landscape.cpp:303
Functions related to OTTD's landscape.
Cached, frequently calculated values.
uint32_t cached_weight
Total weight 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).
uint16_t cached_axle_resistance
Resistance caused by the axles of the vehicle (valid only for the first engine).
EngineID first_engine
Cached EngineID of the front vehicle. INVALID_ENGINE for the front vehicle itself.
uint16_t last_speed
The last speed we did display, so we only have to redraw when this changes.
uint32_t cached_power
Total power of the consist (valid only for the first engine).
uint16_t cached_total_length
Length of the whole vehicle (valid only for the first engine).
uint8_t cached_veh_length
Length of this vehicle in units of 1/VEHICLE_LENGTH of normal length. It is cached because this can b...
uint32_t cached_max_te
Maximum tractive effort of consist (valid only for the first engine).
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_slope_resistance
Resistance caused by weight when this vehicle part is at a slope.
Base class for all vehicles that move through ground.
bool IsChainInDepot() const override
Check whether the whole vehicle chain is in the depot.
void SetFreeWagon()
Set a vehicle as a free wagon.
int UpdateInclination(bool new_tile, bool update_delta)
Checks if the vehicle is in a slope and sets the required flags in that case.
void ClearWagon()
Clear wagon property.
GroundVehicle< T, Type > GroundVehicleBase
Our type.
GroundVehicleCache gcache
Cache of often calculated values.
void CargoChanged()
Recalculates the cached weight of a vehicle and its parts.
void SetMultiheaded()
Set a vehicle as a multiheaded engine.
bool IsEngine() const
Check if a vehicle is an engine (can be first in a consist).
bool IsRearDualheaded() const
Tell if we are dealing with the rear end of a multiheaded engine.
bool IsMultiheaded() const
Check if the vehicle is a multiheaded engine.
void SetEngine()
Set engine status.
void SetWagon()
Set a vehicle to be a wagon.
void UpdateZPositionAndInclination()
Updates vehicle's Z position and inclination.
void SetFrontEngine()
Set front engine state.
void ClearFreeWagon()
Clear a vehicle from being a free wagon.
bool IsWagon() const
Check if a vehicle is a wagon.
int64_t GetSlopeResistance() const
Calculates the total slope resistance for this vehicle.
void PowerChanged()
Recalculates the cached total power of a vehicle.
void ClearArticulatedPart()
Clear a vehicle from being an articulated part.
void SetArticulatedPart()
Set a vehicle to be an articulated part.
uint Crash(bool flooded) override
Common code executed for crashed ground vehicles.
bool IsFreeWagon() const
Check if the vehicle is a free wagon (got no engine in front of it).
void ClearEngine()
Clear engine status.
void ClearMultiheaded()
Clear multiheaded engine property.
uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
Update the speed of the vehicle.
GroundVehicle()
The constructor at SpecializedVehicle must be called.
int GetAcceleration() const
Calculates the acceleration of the vehicle under its current conditions.
void ClearFrontEngine()
Remove the front engine state.
void UpdateZPosition()
Updates vehicle's Z position.
void SetLastSpeed()
Update the GUI variant of the current speed of the vehicle.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
Class defining several overloaded accessors so we don't have to cast vehicle types that often.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
static uint GetAdvanceSpeed(uint speed)
Determines the effective vehicle movement speed.
Definition: vehicle_base.h:445
int32_t z_pos
z coordinate.
Definition: vehicle_base.h:306
Direction direction
facing
Definition: vehicle_base.h:307
virtual uint Crash(bool flooded=false)
Crash the (whole) vehicle chain.
Definition: vehicle.cpp:280
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
Definition: vehicle_base.h:355
uint8_t subspeed
fractional speed
Definition: vehicle_base.h:329
int32_t y_pos
y coordinate.
Definition: vehicle_base.h:305
int32_t x_pos
x coordinate.
Definition: vehicle_base.h:304
uint16_t cur_speed
current speed
Definition: vehicle_base.h:328
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
Definition: vehicle_base.h:332
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
static const uint TILE_UNIT_MASK
For masking in/out the inner-tile world coordinate units.
Definition: tile_type.h:16
Base class for all vehicles.
@ GVSF_ARTICULATED_PART
Articulated part of an engine.
Definition: vehicle_base.h:117
@ GVSF_FRONT
Leading engine of a consist.
Definition: vehicle_base.h:116
@ GVSF_MULTIHEADED
Engine is multiheaded (not used for road vehicles).
Definition: vehicle_base.h:121
@ GVSF_FREE_WAGON
First in a wagon chain (in depot) (not used for road vehicles).
Definition: vehicle_base.h:120
@ GVSF_WAGON
Wagon (not used for road vehicles).
Definition: vehicle_base.h:118
@ GVSF_ENGINE
Engine that can be front engine, but might be placed behind another engine (not used for road vehicle...
Definition: vehicle_base.h:119
Functions related to the vehicle's GUIs.
Types related to the vehicle widgets.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting)
Definition: window.cpp:3106
Window functions not directly related to making/drawing windows.
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
Definition: window_type.h:339