OpenTTD Source 20260711-master-g3fb3006dff
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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
19
21enum AccelStatus : uint8_t {
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 = 0;
34 uint32_t cached_max_te = 0;
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 = 0;
40 uint32_t cached_air_drag = 0;
41
42 /* Cached NewGRF values, recalculated on load and each time a vehicle is added to/removed from the consist. */
43 uint16_t cached_total_length = 0;
44 EngineID first_engine = EngineID::Invalid();
45 uint8_t cached_veh_length = 0;
46
47 /* Cached UI information. */
48 uint16_t last_speed = 0;
49
50 auto operator<=>(const GroundVehicleCache &) const = default;
51};
52
74template <class T, VehicleType Type>
75struct GroundVehicle : public SpecializedVehicle<T, Type> {
78
80
86
87 void PowerChanged();
88 void CargoChanged();
89 int GetAcceleration() const;
90 bool IsChainInDepot() const override;
91
97 uint Crash(bool flooded) override
98 {
99 /* Crashed vehicles aren't going up or down */
100 for (T *v = T::From(this); v != nullptr; v = v->Next()) {
102 }
103 return this->Vehicle::Crash(flooded);
104 }
105
110 inline int64_t GetSlopeResistance() const
111 {
112 int64_t incl = 0;
113
114 for (const T *u = T::From(this); u != nullptr; u = u->Next()) {
115 if (u->gv_flags.Test(GroundVehicleFlag::GoingUp)) {
116 incl += u->gcache.cached_slope_resistance;
117 } else if (u->gv_flags.Test(GroundVehicleFlag::GoingDown)) {
118 incl -= u->gcache.cached_slope_resistance;
119 }
120 }
121
122 return incl;
123 }
124
132 {
133 this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true);
135
136 if (T::From(this)->TileMayHaveSlopedTrack()) {
137 /* To check whether the current tile is sloped, and in which
138 * direction it is sloped, we get the 'z' at the center of
139 * the tile (middle_z) and the edge of the tile (old_z),
140 * which we then can compare. */
141 int middle_z = GetSlopePixelZ((this->x_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), (this->y_pos & ~TILE_UNIT_MASK) | (TILE_SIZE / 2), true);
142
143 if (middle_z != this->z_pos) {
144 this->gv_flags.Set((middle_z > this->z_pos) ? GroundVehicleFlag::GoingUp : GroundVehicleFlag::GoingDown);
145 }
146 }
147 }
148
155 inline void UpdateZPosition()
156 {
157#if 0
158 /* The following code does this: */
159
160 if (this->gv_flags.Test(GroundVehicleFlag::GoingUp)) {
161 switch (this->GetMovingDirection()) {
162 case Direction::NE:
163 this->z_pos += (this->x_pos & 1) ^ 1; break;
164 case Direction::SW:
165 this->z_pos += (this->x_pos & 1); break;
166 case Direction::NW:
167 this->z_pos += (this->y_pos & 1) ^ 1; break;
168 case Direction::SE:
169 this->z_pos += (this->y_pos & 1); break;
170 default: break;
171 }
172 } else if (this->gv_flags.Test(GroundVehicleFlag::GoingDown)) {
173 switch (this->GetMovingDirection()) {
174 case Direction::NE:
175 this->z_pos -= (this->x_pos & 1) ^ 1; break;
176 case Direction::SW:
177 this->z_pos -= (this->x_pos & 1); break;
178 case Direction::NW:
179 this->z_pos -= (this->y_pos & 1) ^ 1; break;
180 case Direction::SE:
181 this->z_pos -= (this->y_pos & 1); break;
182 default: break;
183 }
184 }
185
186 /* But gcc 4.4.5 isn't able to nicely optimise it, and the resulting
187 * code is full of conditional jumps. */
188#endif
189
190 /* Vehicle's Z position can change only if it has GroundVehicleFlag::GoingUp or GroundVehicleFlag::GoingDown set.
191 * Furthermore, if this function is called once every time the vehicle's position changes,
192 * we know the Z position changes by +/-1 at certain moments - when x_pos, y_pos is odd/even,
193 * depending on orientation of the slope and vehicle's direction */
194
195 if (this->gv_flags.Any({GroundVehicleFlag::GoingUp, GroundVehicleFlag::GoingDown})) {
196 if (T::From(this)->HasToUseGetSlopePixelZ()) {
197 /* In some cases, we have to use GetSlopePixelZ() */
198 this->z_pos = GetSlopePixelZ(this->x_pos, this->y_pos, true);
199 return;
200 }
201 /* DirToDiagDir() is a simple right shift */
203 /* Read variables, so the compiler knows the access doesn't trap */
204 int8_t x_pos = this->x_pos;
205 int8_t y_pos = this->y_pos;
206 /* DiagDirToAxis() is a simple mask */
207 int8_t d = DiagDirToAxis(dir) == Axis::X ? x_pos : y_pos;
208 /* We need only the least significant bit */
209 d &= 1;
210 d ^= (int8_t)(dir == DiagDirection::NW || dir == DiagDirection::NE);
211 /* Subtraction instead of addition because we are testing for GroundVehicleFlag::GoingUp.
212 * GroundVehicleFlag::GoingUp is used because it's bit 0, so simple AND can be used,
213 * without any shift */
214 this->z_pos += this->gv_flags.Test(GroundVehicleFlag::GoingUp) ? d : -d;
215 }
216
217 assert(this->z_pos == GetSlopePixelZ(this->x_pos, this->y_pos, true));
218 }
219
226 inline int UpdateInclination(bool new_tile, bool update_delta)
227 {
228 int old_z = this->z_pos;
229
230 if (new_tile) {
232 } else {
233 this->UpdateZPosition();
234 }
235
236 this->UpdateViewport(true, update_delta);
237 return old_z;
238 }
239
243 inline void SetFrontEngine() { SetBit(this->subtype, GVSF_FRONT); }
244
248 inline void ClearFrontEngine() { ClrBit(this->subtype, GVSF_FRONT); }
249
254
259
263 inline void SetWagon() { SetBit(this->subtype, GVSF_WAGON); }
264
268 inline void ClearWagon() { ClrBit(this->subtype, GVSF_WAGON); }
269
273 inline void SetEngine() { SetBit(this->subtype, GVSF_ENGINE); }
274
278 inline void ClearEngine() { ClrBit(this->subtype, GVSF_ENGINE); }
279
283 inline void SetFreeWagon() { SetBit(this->subtype, GVSF_FREE_WAGON); }
284
288 inline void ClearFreeWagon() { ClrBit(this->subtype, GVSF_FREE_WAGON); }
289
294
299
304 inline bool IsFreeWagon() const { return HasBit(this->subtype, GVSF_FREE_WAGON); }
305
310 inline bool IsEngine() const { return HasBit(this->subtype, GVSF_ENGINE); }
311
316 inline bool IsWagon() const { return HasBit(this->subtype, GVSF_WAGON); }
317
322 inline bool IsMultiheaded() const { return HasBit(this->subtype, GVSF_MULTIHEADED); }
323
328 inline bool IsRearDualheaded() const { return this->IsMultiheaded() && !this->IsEngine(); }
329
334 inline bool CanLeadTrain() const
335 {
336 /* NewGRFs can allow unpowered wagons to lead trains. */
337 if (this->GetEngine()->info.extra_flags.Test(ExtraEngineFlag::HasCab)) return true;
338
339 /* This might be an articulated engine. */
340 if (this->IsArticulatedPart()) {
341 return this->GetFirstEnginePart()->IsEngine();
342 }
343
344 return this->IsEngine() || this->IsRearDualheaded();
345 }
346
352 inline void SetLastSpeed()
353 {
354 if (this->cur_speed != this->gcache.last_speed) {
355 SetWindowWidgetDirty(WindowClass::VehicleView, this->index, WID_VV_START_STOP);
356 this->gcache.last_speed = this->cur_speed;
357 }
358 }
359
360protected:
374 inline uint DoUpdateSpeed(uint accel, int min_speed, int max_speed)
375 {
376 uint spd = this->subspeed + accel;
377 this->subspeed = (uint8_t)spd;
378
379 /* When we are going faster than the maximum speed, reduce the speed
380 * somewhat gradually. But never lower than the maximum speed. */
381 int tempmax = max_speed;
382 if (this->cur_speed > max_speed) {
383 tempmax = std::max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
384 }
385
386 /* Enforce a maximum and minimum speed. Normally we would use something like
387 * Clamp for this, but in this case min_speed might be below the maximum speed
388 * threshold for some reason. That makes acceleration fail and assertions
389 * happen in Clamp. So make it explicit that min_speed overrules the maximum
390 * speed by explicit ordering of min and max. */
391 this->cur_speed = spd = std::max(std::min(this->cur_speed + ((int)spd >> 8), tempmax), min_speed);
392
393 int scaled_spd = this->GetAdvanceSpeed(spd);
394
395 scaled_spd += this->progress;
396 this->progress = 0; // set later in *Handler or *Controller
397 return scaled_spd;
398 }
399};
400
401#endif /* GROUND_VEHICLE_HPP */
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
@ SW
Southwest.
@ NW
Northwest.
@ NE
Northeast.
@ SE
Southeast.
@ X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ NW
Northwest.
@ NE
Northeast, upper right on your monitor.
@ HasCab
Train wagon has a cab and can lead a train when backing up, without any speed reduction.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
#define T
Climate temperate.
Definition engines.h:91
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.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
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. EngineID::Invalid() 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.
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.
bool CanLeadTrain() const
Check if this vehicle can lead a train.
void ClearWagon()
Clear wagon property.
GroundVehicle(VehicleID index)
The constructor at SpecializedVehicle must be called.
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.
GroundVehicleFlags gv_flags
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.
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.
SpecializedVehicle(VehicleID index)
Set vehicle type correctly.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
T * GetFirstEnginePart()
Get the first part of an articulated engine.
static uint GetAdvanceSpeed(uint speed)
Determines the effective vehicle movement speed.
int32_t z_pos
z coordinate.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:748
Direction GetMovingDirection() const
Get the moving direction of this vehicle chain.
virtual uint Crash(bool flooded=false)
Crash the (whole) vehicle chain.
Definition vehicle.cpp:300
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
uint8_t subspeed
fractional speed
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint16_t cur_speed
current speed
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
static constexpr uint TILE_UNIT_MASK
For masking in/out the inner-tile world coordinate units.
Definition tile_type.h:16
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
Base class for all vehicles.
@ GVSF_ARTICULATED_PART
Articulated part of an engine.
@ GVSF_FRONT
Leading engine of a consist.
@ GVSF_MULTIHEADED
Engine is multiheaded (not used for road vehicles).
@ GVSF_FREE_WAGON
First in a wagon chain (in depot) (not used for road vehicles).
@ GVSF_WAGON
Wagon (not used for road vehicles).
@ GVSF_ENGINE
Engine that can be front engine, but might be placed behind another engine (not used for road vehicle...
Functions related to the vehicle's GUIs.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
EnumBitSet< GroundVehicleFlag, uint16_t > GroundVehicleFlags
Bitset of GroundVehicleFlag elements.
@ GoingUp
Vehicle is currently going uphill. (Cached track information for acceleration).
@ GoingDown
Vehicle is currently going downhill. (Cached track information for acceleration).
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:3209
Window functions not directly related to making/drawing windows.