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/>.
8 /** @file vehicle.cpp Base implementations of all vehicles. */
14 #include "spritecache.h"
15 #include "timetable.h"
16 #include "viewport_func.h"
17 #include "news_func.h"
18 #include "command_func.h"
19 #include "company_func.h"
22 #include "newgrf_debug.h"
23 #include "newgrf_sound.h"
24 #include "newgrf_station.h"
25 #include "group_gui.h"
26 #include "strings_func.h"
27 #include "zoom_func.h"
28 #include "date_func.h"
29 #include "vehicle_func.h"
30 #include "autoreplace_func.h"
31 #include "autoreplace_gui.h"
32 #include "station_base.h"
34 #include "depot_func.h"
35 #include "network/network.h"
36 #include "core/pool_func.hpp"
37 #include "economy_base.h"
38 #include "articulated_vehicles.h"
39 #include "roadstop_base.h"
40 #include "core/random_func.hpp"
41 #include "core/backup_type.hpp"
42 #include "order_backup.h"
43 #include "sound_func.h"
44 #include "effectvehicle_func.h"
45 #include "effectvehicle_base.h"
46 #include "vehiclelist.h"
47 #include "bridge_map.h"
48 #include "tunnel_map.h"
49 #include "depot_map.h"
51 #include "linkgraph/linkgraph.h"
52 #include "linkgraph/refresh.h"
53 #include "framerate_type.h"
55 #include "table/strings.h"
57 #include "safeguards.h"
59 /* Number of bits in the hash to use from each vehicle coord */
60 static const uint GEN_HASHX_BITS
= 6;
61 static const uint GEN_HASHY_BITS
= 6;
63 /* Size of each hash bucket */
64 static const uint GEN_HASHX_BUCKET_BITS
= 7;
65 static const uint GEN_HASHY_BUCKET_BITS
= 6;
67 /* Compute hash for vehicle coord */
68 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
69 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
70 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
72 /* Maximum size until hash repeats */
73 static const int GEN_HASHX_SIZE
= 1 << (GEN_HASHX_BUCKET_BITS
+ GEN_HASHX_BITS
+ ZOOM_LVL_SHIFT
);
74 static const int GEN_HASHY_SIZE
= 1 << (GEN_HASHY_BUCKET_BITS
+ GEN_HASHY_BITS
+ ZOOM_LVL_SHIFT
);
76 /* Increments to reach next bucket in hash table */
77 static const int GEN_HASHX_INC
= 1;
78 static const int GEN_HASHY_INC
= 1 << GEN_HASHX_BITS
;
80 /* Mask to wrap-around buckets */
81 static const uint GEN_HASHX_MASK
= (1 << GEN_HASHX_BITS
) - 1;
82 static const uint GEN_HASHY_MASK
= ((1 << GEN_HASHY_BITS
) - 1) << GEN_HASHX_BITS
;
84 VehicleID _new_vehicle_id
;
85 uint _returned_refit_capacity
; ///< Stores the capacity after a refit operation.
86 uint16 _returned_mail_refit_capacity
; ///< Stores the mail capacity after a refit operation (Aircraft only).
89 /** The pool with all our precious vehicles. */
90 VehiclePool
_vehicle_pool("Vehicle");
91 INSTANTIATE_POOL_METHODS(Vehicle
)
95 * Determine shared bounds of all sprites.
96 * @param[out] bounds Shared bounds.
98 void VehicleSpriteSeq::GetBounds(Rect
*bounds
) const
100 bounds
->left
= bounds
->top
= bounds
->right
= bounds
->bottom
= 0;
101 for (uint i
= 0; i
< this->count
; ++i
) {
102 const Sprite
*spr
= GetSprite(this->seq
[i
].sprite
, ST_NORMAL
);
104 bounds
->left
= spr
->x_offs
;
105 bounds
->top
= spr
->y_offs
;
106 bounds
->right
= spr
->width
+ spr
->x_offs
- 1;
107 bounds
->bottom
= spr
->height
+ spr
->y_offs
- 1;
109 if (spr
->x_offs
< bounds
->left
) bounds
->left
= spr
->x_offs
;
110 if (spr
->y_offs
< bounds
->top
) bounds
->top
= spr
->y_offs
;
111 int right
= spr
->width
+ spr
->x_offs
- 1;
112 int bottom
= spr
->height
+ spr
->y_offs
- 1;
113 if (right
> bounds
->right
) bounds
->right
= right
;
114 if (bottom
> bounds
->bottom
) bounds
->bottom
= bottom
;
120 * Draw the sprite sequence.
121 * @param x X position
122 * @param y Y position
123 * @param default_pal Vehicle palette
124 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
126 void VehicleSpriteSeq::Draw(int x
, int y
, PaletteID default_pal
, bool force_pal
) const
128 for (uint i
= 0; i
< this->count
; ++i
) {
129 PaletteID pal
= force_pal
|| !this->seq
[i
].pal
? default_pal
: this->seq
[i
].pal
;
130 DrawSprite(this->seq
[i
].sprite
, pal
, x
, y
);
135 * Function to tell if a vehicle needs to be autorenewed
136 * @param *c The vehicle owner
137 * @param use_renew_setting Should the company renew setting be considered?
138 * @return true if the vehicle is old enough for replacement
140 bool Vehicle::NeedsAutorenewing(const Company
*c
, bool use_renew_setting
) const
142 /* We can always generate the Company pointer when we have the vehicle.
143 * However this takes time and since the Company pointer is often present
144 * when this function is called then it's faster to pass the pointer as an
145 * argument rather than finding it again. */
146 assert(c
== Company::Get(this->owner
));
148 if (use_renew_setting
&& !c
->settings
.engine_renew
) return false;
149 if (this->age
- this->max_age
< (c
->settings
.engine_renew_months
* 30)) return false;
151 /* Only engines need renewing */
152 if (this->type
== VEH_TRAIN
&& !Train::From(this)->IsEngine()) return false;
158 * Service a vehicle and all subsequent vehicles in the consist
160 * @param *v The vehicle or vehicle chain being serviced
162 void VehicleServiceInDepot(Vehicle
*v
)
164 assert(v
!= nullptr);
165 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
); // ensure that last service date and reliability are updated
168 v
->date_of_last_service
= _date
;
169 v
->breakdowns_since_last_service
= 0;
170 v
->reliability
= v
->GetEngine()->reliability
;
171 /* Prevent vehicles from breaking down directly after exiting the depot. */
172 v
->breakdown_chance
/= 4;
173 if (_settings_game
.difficulty
.vehicle_breakdowns
== 1) v
->breakdown_chance
= 0; // on reduced breakdown
175 } while (v
!= nullptr && v
->HasEngineType());
179 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
181 * @see NeedsAutomaticServicing()
182 * @return true if the vehicle should go to a depot if a opportunity presents itself.
184 bool Vehicle::NeedsServicing() const
186 /* Stopped or crashed vehicles will not move, as such making unmovable
187 * vehicles to go for service is lame. */
188 if (this->vehstatus
& (VS_STOPPED
| VS_CRASHED
)) return false;
190 /* Are we ready for the next service cycle? */
191 const Company
*c
= Company::Get(this->owner
);
192 if (this->ServiceIntervalIsPercent() ?
193 (this->reliability
>= this->GetEngine()->reliability
* (100 - this->GetServiceInterval()) / 100) :
194 (this->date_of_last_service
+ this->GetServiceInterval() >= _date
)) {
198 /* If we're servicing anyway, because we have not disabled servicing when
199 * there are no breakdowns or we are playing with breakdowns, bail out. */
200 if (!_settings_game
.order
.no_servicing_if_no_breakdowns
||
201 _settings_game
.difficulty
.vehicle_breakdowns
!= 0) {
205 /* Test whether there is some pending autoreplace.
206 * Note: We do this after the service-interval test.
207 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
208 bool pending_replace
= false;
209 Money needed_money
= c
->settings
.engine_renew_money
;
210 if (needed_money
> c
->money
) return false;
212 for (const Vehicle
*v
= this; v
!= nullptr; v
= (v
->type
== VEH_TRAIN
) ? Train::From(v
)->GetNextUnit() : nullptr) {
213 bool replace_when_old
= false;
214 EngineID new_engine
= EngineReplacementForCompany(c
, v
->engine_type
, v
->group_id
, &replace_when_old
);
216 /* Check engine availability */
217 if (new_engine
== INVALID_ENGINE
|| !HasBit(Engine::Get(new_engine
)->company_avail
, v
->owner
)) continue;
218 /* Is the vehicle old if we are not always replacing? */
219 if (replace_when_old
&& !v
->NeedsAutorenewing(c
, false)) continue;
221 /* Check refittability */
222 CargoTypes available_cargo_types
, union_mask
;
223 GetArticulatedRefitMasks(new_engine
, true, &union_mask
, &available_cargo_types
);
224 /* Is there anything to refit? */
225 if (union_mask
!= 0) {
227 /* We cannot refit to mixed cargoes in an automated way */
228 if (IsArticulatedVehicleCarryingDifferentCargoes(v
, &cargo_type
)) continue;
230 /* Did the old vehicle carry anything? */
231 if (cargo_type
!= CT_INVALID
) {
232 /* We can't refit the vehicle to carry the cargo we want */
233 if (!HasBit(available_cargo_types
, cargo_type
)) continue;
238 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
239 pending_replace
= true;
240 needed_money
+= 2 * Engine::Get(new_engine
)->GetCost();
241 if (needed_money
> c
->money
) return false;
244 return pending_replace
;
248 * Checks if the current order should be interrupted for a service-in-depot order.
249 * @see NeedsServicing()
250 * @return true if the current order should be interrupted.
252 bool Vehicle::NeedsAutomaticServicing() const
254 if (this->HasDepotOrder()) return false;
255 if (this->current_order
.IsType(OT_LOADING
)) return false;
256 if (this->current_order
.IsType(OT_GOTO_DEPOT
) && this->current_order
.GetDepotOrderType() != ODTFB_SERVICE
) return false;
257 return NeedsServicing();
260 uint
Vehicle::Crash(bool flooded
)
262 assert((this->vehstatus
& VS_CRASHED
) == 0);
263 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
266 /* Stop the vehicle. */
267 if (this->IsPrimaryVehicle()) this->vehstatus
|= VS_STOPPED
;
268 /* crash all wagons, and count passengers */
269 for (Vehicle
*v
= this; v
!= nullptr; v
= v
->Next()) {
270 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
271 if (IsCargoInClass(v
->cargo_type
, CC_PASSENGERS
)) pass
+= v
->cargo
.TotalCount();
272 v
->vehstatus
|= VS_CRASHED
;
273 v
->MarkAllViewportsDirty();
276 /* Dirty some windows */
277 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
278 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
279 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
280 SetWindowDirty(WC_VEHICLE_DEPOT
, this->tile
);
282 delete this->cargo_payment
;
283 assert(this->cargo_payment
== nullptr); // cleared by ~CargoPayment
285 return RandomRange(pass
+ 1); // Randomise deceased passengers.
290 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
291 * @param engine The engine that caused the problem
292 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
293 * @param part2 Part 2 of the error message, taking the engine as parameter 2
294 * @param bug_type Flag to check and set in grfconfig
295 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
297 void ShowNewGrfVehicleError(EngineID engine
, StringID part1
, StringID part2
, GRFBugs bug_type
, bool critical
)
299 const Engine
*e
= Engine::Get(engine
);
300 GRFConfig
*grfconfig
= GetGRFConfig(e
->GetGRFID());
302 /* Missing GRF. Nothing useful can be done in this situation. */
303 if (grfconfig
== nullptr) return;
305 if (!HasBit(grfconfig
->grf_bugs
, bug_type
)) {
306 SetBit(grfconfig
->grf_bugs
, bug_type
);
307 SetDParamStr(0, grfconfig
->GetName());
308 SetDParam(1, engine
);
309 ShowErrorMessage(part1
, part2
, WL_CRITICAL
);
310 if (!_networking
) DoCommand(0, critical
? PM_PAUSED_ERROR
: PM_PAUSED_NORMAL
, 1, DC_EXEC
, CMD_PAUSE
);
316 SetDParamStr(0, grfconfig
->GetName());
317 GetString(buffer
, part1
, lastof(buffer
));
318 DEBUG(grf
, 0, "%s", buffer
+ 3);
320 SetDParam(1, engine
);
321 GetString(buffer
, part2
, lastof(buffer
));
322 DEBUG(grf
, 0, "%s", buffer
+ 3);
326 * Logs a bug in GRF and shows a warning message if this
327 * is for the first time this happened.
328 * @param u first vehicle of chain
330 void VehicleLengthChanged(const Vehicle
*u
)
332 /* show a warning once for each engine in whole game and once for each GRF after each game load */
333 const Engine
*engine
= u
->GetEngine();
334 uint32 grfid
= engine
->grf_prop
.grffile
->grfid
;
335 GRFConfig
*grfconfig
= GetGRFConfig(grfid
);
336 if (GamelogGRFBugReverse(grfid
, engine
->grf_prop
.local_id
) || !HasBit(grfconfig
->grf_bugs
, GBUG_VEH_LENGTH
)) {
337 ShowNewGrfVehicleError(u
->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_VEHICLE_LENGTH
, GBUG_VEH_LENGTH
, true);
342 * Vehicle constructor.
343 * @param type Type of the new vehicle.
345 Vehicle::Vehicle(VehicleType type
)
348 this->coord
.left
= INVALID_COORD
;
349 this->sprite_cache
.old_coord
.left
= INVALID_COORD
;
350 this->group_id
= DEFAULT_GROUP
;
351 this->fill_percent_te_id
= INVALID_TE_ID
;
353 this->colourmap
= PAL_NONE
;
354 this->cargo_age_counter
= 1;
355 this->last_station_visited
= INVALID_STATION
;
356 this->last_loading_station
= INVALID_STATION
;
360 * Get a value for a vehicle's random_bits.
361 * @return A random value from 0 to 255.
363 byte
VehicleRandomBits()
365 return GB(Random(), 0, 8);
368 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
369 * lookup times at the expense of memory usage. */
370 const int HASH_BITS
= 7;
371 const int HASH_SIZE
= 1 << HASH_BITS
;
372 const int HASH_MASK
= HASH_SIZE
- 1;
373 const int TOTAL_HASH_SIZE
= 1 << (HASH_BITS
* 2);
374 const int TOTAL_HASH_MASK
= TOTAL_HASH_SIZE
- 1;
376 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
377 * Profiling results show that 0 is fastest. */
378 const int HASH_RES
= 0;
380 static Vehicle
*_vehicle_tile_hash
[TOTAL_HASH_SIZE
];
382 static Vehicle
*VehicleFromTileHash(int xl
, int yl
, int xu
, int yu
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
384 for (int y
= yl
; ; y
= (y
+ (1 << HASH_BITS
)) & (HASH_MASK
<< HASH_BITS
)) {
385 for (int x
= xl
; ; x
= (x
+ 1) & HASH_MASK
) {
386 Vehicle
*v
= _vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
387 for (; v
!= nullptr; v
= v
->hash_tile_next
) {
388 Vehicle
*a
= proc(v
, data
);
389 if (find_first
&& a
!= nullptr) return a
;
401 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
402 * @note Do not call this function directly!
403 * @param x The X location on the map
404 * @param y The Y location on the map
405 * @param data Arbitrary data passed to proc
406 * @param proc The proc that determines whether a vehicle will be "found".
407 * @param find_first Whether to return on the first found or iterate over
409 * @return the best matching or first vehicle (depending on find_first).
411 static Vehicle
*VehicleFromPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
413 const int COLL_DIST
= 6;
415 /* Hash area to scan is from xl,yl to xu,yu */
416 int xl
= GB((x
- COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
);
417 int xu
= GB((x
+ COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
);
418 int yl
= GB((y
- COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
) << HASH_BITS
;
419 int yu
= GB((y
+ COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
) << HASH_BITS
;
421 return VehicleFromTileHash(xl
, yl
, xu
, yu
, data
, proc
, find_first
);
425 * Find a vehicle from a specific location. It will call proc for ALL vehicles
426 * on the tile and YOU must make SURE that the "best one" is stored in the
427 * data value and is ALWAYS the same regardless of the order of the vehicles
428 * where proc was called on!
429 * When you fail to do this properly you create an almost untraceable DESYNC!
430 * @note The return value of proc will be ignored.
431 * @note Use this when you have the intention that all vehicles
432 * should be iterated over.
433 * @param x The X location on the map
434 * @param y The Y location on the map
435 * @param data Arbitrary data passed to proc
436 * @param proc The proc that determines whether a vehicle will be "found".
438 void FindVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
440 VehicleFromPosXY(x
, y
, data
, proc
, false);
444 * Checks whether a vehicle in on a specific location. It will call proc for
445 * vehicles until it returns non-nullptr.
446 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
447 * should be iterated over.
448 * @param x The X location on the map
449 * @param y The Y location on the map
450 * @param data Arbitrary data passed to proc
451 * @param proc The proc that determines whether a vehicle will be "found".
452 * @return True if proc returned non-nullptr.
454 bool HasVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
456 return VehicleFromPosXY(x
, y
, data
, proc
, true) != nullptr;
460 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
461 * @note Do not call this function directly!
462 * @param tile The location on the map
463 * @param data Arbitrary data passed to \a proc.
464 * @param proc The proc that determines whether a vehicle will be "found".
465 * @param find_first Whether to return on the first found or iterate over
467 * @return the best matching or first vehicle (depending on find_first).
469 static Vehicle
*VehicleFromPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
471 int x
= GB(TileX(tile
), HASH_RES
, HASH_BITS
);
472 int y
= GB(TileY(tile
), HASH_RES
, HASH_BITS
) << HASH_BITS
;
474 Vehicle
*v
= _vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
475 for (; v
!= nullptr; v
= v
->hash_tile_next
) {
476 if (v
->tile
!= tile
) continue;
478 Vehicle
*a
= proc(v
, data
);
479 if (find_first
&& a
!= nullptr) return a
;
486 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
487 * on the tile and YOU must make SURE that the "best one" is stored in the
488 * data value and is ALWAYS the same regardless of the order of the vehicles
489 * where proc was called on!
490 * When you fail to do this properly you create an almost untraceable DESYNC!
491 * @note The return value of \a proc will be ignored.
492 * @note Use this function when you have the intention that all vehicles
493 * should be iterated over.
494 * @param tile The location on the map
495 * @param data Arbitrary data passed to \a proc.
496 * @param proc The proc that determines whether a vehicle will be "found".
498 void FindVehicleOnPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
)
500 VehicleFromPos(tile
, data
, proc
, false);
504 * Checks whether a vehicle is on a specific location. It will call \a proc for
505 * vehicles until it returns non-nullptr.
506 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
507 * should be iterated over.
508 * @param tile The location on the map
509 * @param data Arbitrary data passed to \a proc.
510 * @param proc The \a proc that determines whether a vehicle will be "found".
511 * @return True if proc returned non-nullptr.
513 bool HasVehicleOnPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
)
515 return VehicleFromPos(tile
, data
, proc
, true) != nullptr;
519 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
520 * @param v Vehicle to examine.
521 * @param data Pointer to height data.
522 * @return \a v if conditions are met, else \c nullptr.
524 static Vehicle
*EnsureNoVehicleProcZ(Vehicle
*v
, void *data
)
528 if (v
->type
== VEH_DISASTER
|| (v
->type
== VEH_AIRCRAFT
&& v
->subtype
== AIR_SHADOW
)) return nullptr;
529 if (v
->z_pos
> z
) return nullptr;
535 * Ensure there is no vehicle at the ground at the given position.
536 * @param tile Position to examine.
537 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
539 CommandCost
EnsureNoVehicleOnGround(TileIndex tile
)
541 int z
= GetTileMaxPixelZ(tile
);
543 /* Value v is not safe in MP games, however, it is used to generate a local
544 * error message only (which may be different for different machines).
545 * Such a message does not affect MP synchronisation.
547 Vehicle
*v
= VehicleFromPos(tile
, &z
, &EnsureNoVehicleProcZ
, true);
548 if (v
!= nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
549 return CommandCost();
552 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
553 static Vehicle
*GetVehicleTunnelBridgeProc(Vehicle
*v
, void *data
)
555 if (v
->type
!= VEH_TRAIN
&& v
->type
!= VEH_ROAD
&& v
->type
!= VEH_SHIP
) return nullptr;
556 if (v
== (const Vehicle
*)data
) return nullptr;
562 * Finds vehicle in tunnel / bridge
563 * @param tile first end
564 * @param endtile second end
565 * @param ignore Ignore this vehicle when searching
566 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
568 CommandCost
TunnelBridgeIsFree(TileIndex tile
, TileIndex endtile
, const Vehicle
*ignore
)
570 /* Value v is not safe in MP games, however, it is used to generate a local
571 * error message only (which may be different for different machines).
572 * Such a message does not affect MP synchronisation.
574 Vehicle
*v
= VehicleFromPos(tile
, const_cast<Vehicle
*>(ignore
), &GetVehicleTunnelBridgeProc
, true);
575 if (v
== nullptr) v
= VehicleFromPos(endtile
, const_cast<Vehicle
*>(ignore
), &GetVehicleTunnelBridgeProc
, true);
577 if (v
!= nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
578 return CommandCost();
581 static Vehicle
*EnsureNoTrainOnTrackProc(Vehicle
*v
, void *data
)
583 TrackBits rail_bits
= *(TrackBits
*)data
;
585 if (v
->type
!= VEH_TRAIN
) return nullptr;
587 Train
*t
= Train::From(v
);
588 if ((t
->track
!= rail_bits
) && !TracksOverlap(t
->track
| rail_bits
)) return nullptr;
594 * Tests if a vehicle interacts with the specified track bits.
595 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
597 * @param tile The tile.
598 * @param track_bits The track bits.
599 * @return \c true if no train that interacts, is found. \c false if a train is found.
601 CommandCost
EnsureNoTrainOnTrackBits(TileIndex tile
, TrackBits track_bits
)
603 /* Value v is not safe in MP games, however, it is used to generate a local
604 * error message only (which may be different for different machines).
605 * Such a message does not affect MP synchronisation.
607 Vehicle
*v
= VehicleFromPos(tile
, &track_bits
, &EnsureNoTrainOnTrackProc
, true);
608 if (v
!= nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
609 return CommandCost();
612 static void UpdateVehicleTileHash(Vehicle
*v
, bool remove
)
614 Vehicle
**old_hash
= v
->hash_tile_current
;
620 int x
= GB(TileX(v
->tile
), HASH_RES
, HASH_BITS
);
621 int y
= GB(TileY(v
->tile
), HASH_RES
, HASH_BITS
) << HASH_BITS
;
622 new_hash
= &_vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
625 if (old_hash
== new_hash
) return;
627 /* Remove from the old position in the hash table */
628 if (old_hash
!= nullptr) {
629 if (v
->hash_tile_next
!= nullptr) v
->hash_tile_next
->hash_tile_prev
= v
->hash_tile_prev
;
630 *v
->hash_tile_prev
= v
->hash_tile_next
;
633 /* Insert vehicle at beginning of the new position in the hash table */
634 if (new_hash
!= nullptr) {
635 v
->hash_tile_next
= *new_hash
;
636 if (v
->hash_tile_next
!= nullptr) v
->hash_tile_next
->hash_tile_prev
= &v
->hash_tile_next
;
637 v
->hash_tile_prev
= new_hash
;
641 /* Remember current hash position */
642 v
->hash_tile_current
= new_hash
;
645 static Vehicle
*_vehicle_viewport_hash
[1 << (GEN_HASHX_BITS
+ GEN_HASHY_BITS
)];
647 static void UpdateVehicleViewportHash(Vehicle
*v
, int x
, int y
, int old_x
, int old_y
)
649 Vehicle
**old_hash
, **new_hash
;
651 new_hash
= (x
== INVALID_COORD
) ? nullptr : &_vehicle_viewport_hash
[GEN_HASH(x
, y
)];
652 old_hash
= (old_x
== INVALID_COORD
) ? nullptr : &_vehicle_viewport_hash
[GEN_HASH(old_x
, old_y
)];
654 if (old_hash
== new_hash
) return;
656 /* remove from hash table? */
657 if (old_hash
!= nullptr) {
658 if (v
->hash_viewport_next
!= nullptr) v
->hash_viewport_next
->hash_viewport_prev
= v
->hash_viewport_prev
;
659 *v
->hash_viewport_prev
= v
->hash_viewport_next
;
662 /* insert into hash table? */
663 if (new_hash
!= nullptr) {
664 v
->hash_viewport_next
= *new_hash
;
665 if (v
->hash_viewport_next
!= nullptr) v
->hash_viewport_next
->hash_viewport_prev
= &v
->hash_viewport_next
;
666 v
->hash_viewport_prev
= new_hash
;
671 void ResetVehicleHash()
673 for (Vehicle
*v
: Vehicle::Iterate()) { v
->hash_tile_current
= nullptr; }
674 memset(_vehicle_viewport_hash
, 0, sizeof(_vehicle_viewport_hash
));
675 memset(_vehicle_tile_hash
, 0, sizeof(_vehicle_tile_hash
));
678 void ResetVehicleColourMap()
680 for (Vehicle
*v
: Vehicle::Iterate()) { v
->colourmap
= PAL_NONE
; }
684 * List of vehicles that should check for autoreplace this tick.
685 * Mapping of vehicle -> leave depot immediately after autoreplace.
687 typedef SmallMap
<Vehicle
*, bool> AutoreplaceMap
;
688 static AutoreplaceMap _vehicles_to_autoreplace
;
690 void InitializeVehicles()
692 _vehicles_to_autoreplace
.clear();
693 _vehicles_to_autoreplace
.shrink_to_fit();
697 uint
CountVehiclesInChain(const Vehicle
*v
)
700 do count
++; while ((v
= v
->Next()) != nullptr);
705 * Check if a vehicle is counted in num_engines in each company struct
706 * @return true if the vehicle is counted in num_engines
708 bool Vehicle::IsEngineCountable() const
710 switch (this->type
) {
711 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
713 return !this->IsArticulatedPart() && // tenders and other articulated parts
714 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
715 case VEH_ROAD
: return RoadVehicle::From(this)->IsFrontEngine();
716 case VEH_SHIP
: return true;
717 default: return false; // Only count company buildable vehicles
722 * Check whether Vehicle::engine_type has any meaning.
723 * @return true if the vehicle has a usable engine type.
725 bool Vehicle::HasEngineType() const
727 switch (this->type
) {
728 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft();
731 case VEH_SHIP
: return true;
732 default: return false;
737 * Retrieves the engine of the vehicle.
738 * @return Engine of the vehicle.
739 * @pre HasEngineType() == true
741 const Engine
*Vehicle::GetEngine() const
743 return Engine::Get(this->engine_type
);
747 * Retrieve the NewGRF the vehicle is tied to.
748 * This is the GRF providing the Action 3 for the engine type.
749 * @return NewGRF associated to the vehicle.
751 const GRFFile
*Vehicle::GetGRF() const
753 return this->GetEngine()->GetGRF();
757 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
758 * This is the GRF providing the Action 3 for the engine type.
759 * @return GRF ID of the associated NewGRF.
761 uint32
Vehicle::GetGRFID() const
763 return this->GetEngine()->GetGRFID();
767 * Handle the pathfinding result, especially the lost status.
768 * If the vehicle is now lost and wasn't previously fire an
769 * event to the AIs and a news message to the user. If the
770 * vehicle is not lost anymore remove the news message.
771 * @param path_found Whether the vehicle has a path to its destination.
773 void Vehicle::HandlePathfindingResult(bool path_found
)
776 /* Route found, is the vehicle marked with "lost" flag? */
777 if (!HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
779 /* Clear the flag as the PF's problem was solved. */
780 ClrBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
781 /* Delete the news item. */
782 DeleteVehicleNews(this->index
, STR_NEWS_VEHICLE_IS_LOST
);
786 /* Were we already lost? */
787 if (HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
789 /* It is first time the problem occurred, set the "lost" flag. */
790 SetBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
791 /* Notify user about the event. */
792 AI::NewEvent(this->owner
, new ScriptEventVehicleLost(this->index
));
793 if (_settings_client
.gui
.lost_vehicle_warn
&& this->owner
== _local_company
) {
794 SetDParam(0, this->index
);
795 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST
, this->index
);
799 /** Destroy all stuff that (still) needs the virtual functions to work properly */
800 void Vehicle::PreDestructor()
802 if (CleaningPool()) return;
804 if (Station::IsValidID(this->last_station_visited
)) {
805 Station
*st
= Station::Get(this->last_station_visited
);
806 st
->loading_vehicles
.remove(this);
808 HideFillingPercent(&this->fill_percent_te_id
);
809 this->CancelReservation(INVALID_STATION
, st
);
810 delete this->cargo_payment
;
811 assert(this->cargo_payment
== nullptr); // cleared by ~CargoPayment
814 if (this->IsEngineCountable()) {
815 GroupStatistics::CountEngine(this, -1);
816 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
817 GroupStatistics::UpdateAutoreplace(this->owner
);
819 if (this->owner
== _local_company
) InvalidateAutoreplaceWindow(this->engine_type
, this->group_id
);
820 DeleteGroupHighlightOfVehicle(this);
823 if (this->type
== VEH_AIRCRAFT
&& this->IsPrimaryVehicle()) {
824 Aircraft
*a
= Aircraft::From(this);
825 Station
*st
= GetTargetAirportIfValid(a
);
827 const AirportFTA
*layout
= st
->airport
.GetFTA()->layout
;
828 CLRBITS(st
->airport
.flags
, layout
[a
->previous_pos
].block
| layout
[a
->pos
].block
);
833 if (this->type
== VEH_ROAD
&& this->IsPrimaryVehicle()) {
834 RoadVehicle
*v
= RoadVehicle::From(this);
835 if (!(v
->vehstatus
& VS_CRASHED
) && IsInsideMM(v
->state
, RVSB_IN_DT_ROAD_STOP
, RVSB_IN_DT_ROAD_STOP_END
)) {
836 /* Leave the drive through roadstop, when you have not already left it. */
837 RoadStop::GetByTile(v
->tile
, GetRoadStopType(v
->tile
))->Leave(v
);
841 if (this->Previous() == nullptr) {
842 InvalidateWindowData(WC_VEHICLE_DEPOT
, this->tile
);
845 if (this->IsPrimaryVehicle()) {
846 DeleteWindowById(WC_VEHICLE_VIEW
, this->index
);
847 DeleteWindowById(WC_VEHICLE_ORDERS
, this->index
);
848 DeleteWindowById(WC_VEHICLE_REFIT
, this->index
);
849 DeleteWindowById(WC_VEHICLE_DETAILS
, this->index
);
850 DeleteWindowById(WC_VEHICLE_TIMETABLE
, this->index
);
851 SetWindowDirty(WC_COMPANY
, this->owner
);
852 OrderBackup::ClearVehicle(this);
854 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
856 this->cargo
.Truncate();
857 DeleteVehicleOrders(this);
858 DeleteDepotHighlightOfVehicle(this);
860 extern void StopGlobalFollowVehicle(const Vehicle
*v
);
861 StopGlobalFollowVehicle(this);
863 ReleaseDisastersTargetingVehicle(this->index
);
868 if (CleaningPool()) {
869 this->cargo
.OnCleanPool();
873 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
874 * it may happen that vehicle chain is deleted when visible */
875 if (!(this->vehstatus
& VS_HIDDEN
)) this->MarkAllViewportsDirty();
877 Vehicle
*v
= this->Next();
878 this->SetNext(nullptr);
882 UpdateVehicleTileHash(this, true);
883 UpdateVehicleViewportHash(this, INVALID_COORD
, 0, this->sprite_cache
.old_coord
.left
, this->sprite_cache
.old_coord
.top
);
884 DeleteVehicleNews(this->index
, INVALID_STRING_ID
);
885 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type
), this->index
);
889 * Adds a vehicle to the list of vehicles that visited a depot this tick
890 * @param *v vehicle to add
892 void VehicleEnteredDepotThisTick(Vehicle
*v
)
894 /* Vehicle should stop in the depot if it was in 'stopping' state */
895 _vehicles_to_autoreplace
[v
] = !(v
->vehstatus
& VS_STOPPED
);
897 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
898 * stopping in the depot, so we stop it to ensure that it will not reserve
899 * the path out of the depot before we might autoreplace it to a different
900 * engine. The new engine would not own the reserved path we store that we
901 * stopped the vehicle, so autoreplace can start it again */
902 v
->vehstatus
|= VS_STOPPED
;
906 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
907 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
908 * so each day, all vehicles are processes in DAY_TICKS steps.
910 static void RunVehicleDayProc()
912 if (_game_mode
!= GM_NORMAL
) return;
914 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
915 for (size_t i
= _date_fract
; i
< Vehicle::GetPoolSize(); i
+= DAY_TICKS
) {
916 Vehicle
*v
= Vehicle::Get(i
);
917 if (v
== nullptr) continue;
919 /* Call the 32-day callback if needed */
920 if ((v
->day_counter
& 0x1F) == 0 && v
->HasEngineType()) {
921 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK
, 0, 0, v
->engine_type
, v
);
922 if (callback
!= CALLBACK_FAILED
) {
923 if (HasBit(callback
, 0)) {
924 TriggerVehicle(v
, VEHICLE_TRIGGER_CALLBACK_32
); // Trigger vehicle trigger 10
927 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
928 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
929 if (callback
!= 0) v
->First()->MarkDirty();
931 if (callback
& ~3) ErrorUnknownCallbackResult(v
->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK
, callback
);
935 /* This is called once per day for each vehicle, but not in the first tick of the day */
940 void CallVehicleTicks()
942 _vehicles_to_autoreplace
.clear();
947 PerformanceMeasurer
framerate(PFE_GL_ECONOMY
);
948 for (Station
*st
: Station::Iterate()) LoadUnloadStation(st
);
950 PerformanceAccumulator::Reset(PFE_GL_TRAINS
);
951 PerformanceAccumulator::Reset(PFE_GL_ROADVEHS
);
952 PerformanceAccumulator::Reset(PFE_GL_SHIPS
);
953 PerformanceAccumulator::Reset(PFE_GL_AIRCRAFT
);
955 for (Vehicle
*v
: Vehicle::Iterate()) {
956 size_t vehicle_index
= v
->index
;
957 /* Vehicle could be deleted in this tick */
959 assert(Vehicle::Get(vehicle_index
) == nullptr);
963 assert(Vehicle::Get(vehicle_index
) == v
);
972 Vehicle
*front
= v
->First();
974 if (v
->vcache
.cached_cargo_age_period
!= 0) {
975 v
->cargo_age_counter
= std::min(v
->cargo_age_counter
, v
->vcache
.cached_cargo_age_period
);
976 if (--v
->cargo_age_counter
== 0) {
978 v
->cargo_age_counter
= v
->vcache
.cached_cargo_age_period
;
982 /* Do not play any sound when crashed */
983 if (front
->vehstatus
& VS_CRASHED
) continue;
985 /* Do not play any sound when in depot or tunnel */
986 if (v
->vehstatus
& VS_HIDDEN
) continue;
988 /* Do not play any sound when stopped */
989 if ((front
->vehstatus
& VS_STOPPED
) && (front
->type
!= VEH_TRAIN
|| front
->cur_speed
== 0)) continue;
991 /* Check vehicle type specifics */
994 if (Train::From(v
)->IsWagon()) continue;
998 if (!RoadVehicle::From(v
)->IsFrontEngine()) continue;
1002 if (!Aircraft::From(v
)->IsNormalAircraft()) continue;
1009 v
->motion_counter
+= front
->cur_speed
;
1010 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1011 if (GB(v
->motion_counter
, 0, 8) < front
->cur_speed
) PlayVehicleSound(v
, VSE_RUNNING
);
1013 /* Play an alternating running sound every 16 ticks */
1014 if (GB(v
->tick_counter
, 0, 4) == 0) {
1015 /* Play running sound when speed > 0 and not braking */
1016 bool running
= (front
->cur_speed
> 0) && !(front
->vehstatus
& (VS_STOPPED
| VS_TRAIN_SLOWING
));
1017 PlayVehicleSound(v
, running
? VSE_RUNNING_16
: VSE_STOPPED_16
);
1025 Backup
<CompanyID
> cur_company(_current_company
, FILE_LINE
);
1026 for (auto &it
: _vehicles_to_autoreplace
) {
1027 Vehicle
*v
= it
.first
;
1028 /* Autoreplace needs the current company set as the vehicle owner */
1029 cur_company
.Change(v
->owner
);
1031 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1032 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1033 * they are already leaving the depot again before being replaced. */
1034 if (it
.second
) v
->vehstatus
&= ~VS_STOPPED
;
1036 /* Store the position of the effect as the vehicle pointer will become invalid later */
1041 const Company
*c
= Company::Get(_current_company
);
1042 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, (Money
)c
->settings
.engine_renew_money
));
1043 CommandCost res
= DoCommand(0, v
->index
, 0, DC_EXEC
, CMD_AUTOREPLACE_VEHICLE
);
1044 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, -(Money
)c
->settings
.engine_renew_money
));
1046 if (!IsLocalCompany()) continue;
1048 if (res
.Succeeded() && res
.GetCost() != 0) {
1049 ShowCostOrIncomeAnimation(x
, y
, z
, res
.GetCost());
1053 StringID error_message
= res
.GetErrorMessage();
1054 if (error_message
== STR_ERROR_AUTOREPLACE_NOTHING_TO_DO
|| error_message
== INVALID_STRING_ID
) continue;
1056 if (error_message
== STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY
) error_message
= STR_ERROR_AUTOREPLACE_MONEY_LIMIT
;
1059 if (error_message
== STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT
) {
1060 message
= error_message
;
1062 message
= STR_NEWS_VEHICLE_AUTORENEW_FAILED
;
1065 SetDParam(0, v
->index
);
1066 SetDParam(1, error_message
);
1067 AddVehicleAdviceNewsItem(message
, v
->index
);
1070 cur_company
.Restore();
1074 * Add vehicle sprite for drawing to the screen.
1075 * @param v Vehicle to draw.
1077 static void DoDrawVehicle(const Vehicle
*v
)
1079 PaletteID pal
= PAL_NONE
;
1081 if (v
->vehstatus
& VS_DEFPAL
) pal
= (v
->vehstatus
& VS_CRASHED
) ? PALETTE_CRASH
: GetVehiclePalette(v
);
1083 /* Check whether the vehicle shall be transparent due to the game state */
1084 bool shadowed
= (v
->vehstatus
& VS_SHADOW
) != 0;
1086 if (v
->type
== VEH_EFFECT
) {
1087 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1088 * However, transparent smoke and bubbles look weird, so always hide them. */
1089 TransparencyOption to
= EffectVehicle::From(v
)->GetTransparencyOption();
1090 if (to
!= TO_INVALID
&& (IsTransparencySet(to
) || IsInvisibilitySet(to
))) return;
1093 StartSpriteCombine();
1094 for (uint i
= 0; i
< v
->sprite_cache
.sprite_seq
.count
; ++i
) {
1095 PaletteID pal2
= v
->sprite_cache
.sprite_seq
.seq
[i
].pal
;
1096 if (!pal2
|| (v
->vehstatus
& VS_CRASHED
)) pal2
= pal
;
1097 AddSortableSpriteToDraw(v
->sprite_cache
.sprite_seq
.seq
[i
].sprite
, pal2
, v
->x_pos
+ v
->x_offs
, v
->y_pos
+ v
->y_offs
,
1098 v
->x_extent
, v
->y_extent
, v
->z_extent
, v
->z_pos
, shadowed
, v
->x_bb_offs
, v
->y_bb_offs
);
1104 * Add the vehicle sprites that should be drawn at a part of the screen.
1105 * @param dpi Rectangle being drawn.
1107 void ViewportAddVehicles(DrawPixelInfo
*dpi
)
1109 /* The bounding rectangle */
1110 const int l
= dpi
->left
;
1111 const int r
= dpi
->left
+ dpi
->width
;
1112 const int t
= dpi
->top
;
1113 const int b
= dpi
->top
+ dpi
->height
;
1115 /* Border size of MAX_VEHICLE_PIXEL_xy */
1116 const int xb
= MAX_VEHICLE_PIXEL_X
* ZOOM_LVL_BASE
;
1117 const int yb
= MAX_VEHICLE_PIXEL_Y
* ZOOM_LVL_BASE
;
1119 /* The hash area to scan */
1122 if (dpi
->width
+ xb
< GEN_HASHX_SIZE
) {
1123 xl
= GEN_HASHX(l
- xb
);
1126 /* scan whole hash row */
1128 xu
= GEN_HASHX_MASK
;
1131 if (dpi
->height
+ yb
< GEN_HASHY_SIZE
) {
1132 yl
= GEN_HASHY(t
- yb
);
1135 /* scan whole column */
1137 yu
= GEN_HASHY_MASK
;
1140 for (int y
= yl
;; y
= (y
+ GEN_HASHY_INC
) & GEN_HASHY_MASK
) {
1141 for (int x
= xl
;; x
= (x
+ GEN_HASHX_INC
) & GEN_HASHX_MASK
) {
1142 const Vehicle
*v
= _vehicle_viewport_hash
[x
+ y
]; // already masked & 0xFFF
1144 while (v
!= nullptr) {
1146 if (!(v
->vehstatus
& VS_HIDDEN
) &&
1147 l
<= v
->coord
.right
+ xb
&&
1148 t
<= v
->coord
.bottom
+ yb
&&
1149 r
>= v
->coord
.left
- xb
&&
1150 b
>= v
->coord
.top
- yb
)
1153 * This vehicle can potentially be drawn as part of this viewport and
1154 * needs to be revalidated, as the sprite may not be correct.
1156 if (v
->sprite_cache
.revalidate_before_draw
) {
1157 VehicleSpriteSeq seq
;
1158 v
->GetImage(v
->direction
, EIT_ON_MAP
, &seq
);
1160 if (seq
.IsValid() && v
->sprite_cache
.sprite_seq
!= seq
) {
1161 v
->sprite_cache
.sprite_seq
= seq
;
1163 * A sprite change may also result in a bounding box change,
1164 * so we need to update the bounding box again before we
1165 * check to see if the vehicle should be drawn. Note that
1166 * we can't interfere with the viewport hash at this point,
1167 * so we keep the original hash on the assumption there will
1168 * not be a significant change in the top and left coordinates
1171 v
->UpdateBoundingBoxCoordinates(false);
1175 v
->sprite_cache
.revalidate_before_draw
= false;
1178 if (l
<= v
->coord
.right
&&
1179 t
<= v
->coord
.bottom
&&
1180 r
>= v
->coord
.left
&&
1181 b
>= v
->coord
.top
) DoDrawVehicle(v
);
1184 v
= v
->hash_viewport_next
;
1195 * Find the vehicle close to the clicked coordinates.
1196 * @param vp Viewport clicked in.
1197 * @param x X coordinate in the viewport.
1198 * @param y Y coordinate in the viewport.
1199 * @return Closest vehicle, or \c nullptr if none found.
1201 Vehicle
*CheckClickOnVehicle(const Viewport
*vp
, int x
, int y
)
1203 Vehicle
*found
= nullptr;
1204 uint dist
, best_dist
= UINT_MAX
;
1206 if ((uint
)(x
-= vp
->left
) >= (uint
)vp
->width
|| (uint
)(y
-= vp
->top
) >= (uint
)vp
->height
) return nullptr;
1208 x
= ScaleByZoom(x
, vp
->zoom
) + vp
->virtual_left
;
1209 y
= ScaleByZoom(y
, vp
->zoom
) + vp
->virtual_top
;
1211 for (Vehicle
*v
: Vehicle::Iterate()) {
1212 if ((v
->vehstatus
& (VS_HIDDEN
| VS_UNCLICKABLE
)) == 0 &&
1213 x
>= v
->coord
.left
&& x
<= v
->coord
.right
&&
1214 y
>= v
->coord
.top
&& y
<= v
->coord
.bottom
) {
1217 abs(((v
->coord
.left
+ v
->coord
.right
) >> 1) - x
),
1218 abs(((v
->coord
.top
+ v
->coord
.bottom
) >> 1) - y
)
1221 if (dist
< best_dist
) {
1232 * Decrease the value of a vehicle.
1233 * @param v %Vehicle to devaluate.
1235 void DecreaseVehicleValue(Vehicle
*v
)
1237 v
->value
-= v
->value
>> 8;
1238 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1241 static const byte _breakdown_chance
[64] = {
1242 3, 3, 3, 3, 3, 3, 3, 3,
1243 4, 4, 5, 5, 6, 6, 7, 7,
1244 8, 8, 9, 9, 10, 10, 11, 11,
1245 12, 13, 13, 13, 13, 14, 15, 16,
1246 17, 19, 21, 25, 28, 31, 34, 37,
1247 40, 44, 48, 52, 56, 60, 64, 68,
1248 72, 80, 90, 100, 110, 120, 130, 140,
1249 150, 170, 190, 210, 230, 250, 250, 250,
1252 void CheckVehicleBreakdown(Vehicle
*v
)
1256 /* decrease reliability */
1257 if (!_settings_game
.order
.no_servicing_if_no_breakdowns
||
1258 _settings_game
.difficulty
.vehicle_breakdowns
!= 0) {
1259 v
->reliability
= rel
= std::max((rel_old
= v
->reliability
) - v
->reliability_spd_dec
, 0);
1260 if ((rel_old
>> 8) != (rel
>> 8)) SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1263 if (v
->breakdown_ctr
!= 0 || (v
->vehstatus
& VS_STOPPED
) ||
1264 _settings_game
.difficulty
.vehicle_breakdowns
< 1 ||
1265 v
->cur_speed
< 5 || _game_mode
== GM_MENU
) {
1269 uint32 r
= Random();
1271 /* increase chance of failure */
1272 int chance
= v
->breakdown_chance
+ 1;
1273 if (Chance16I(1, 25, r
)) chance
+= 25;
1274 v
->breakdown_chance
= std::min(255, chance
);
1276 /* calculate reliability value to use in comparison */
1277 rel
= v
->reliability
;
1278 if (v
->type
== VEH_SHIP
) rel
+= 0x6666;
1280 /* reduced breakdowns? */
1281 if (_settings_game
.difficulty
.vehicle_breakdowns
== 1) rel
+= 0x6666;
1283 /* check if to break down */
1284 if (_breakdown_chance
[(uint
)std::min(rel
, 0xffff) >> 10] <= v
->breakdown_chance
) {
1285 v
->breakdown_ctr
= GB(r
, 16, 6) + 0x3F;
1286 v
->breakdown_delay
= GB(r
, 24, 7) + 0x80;
1287 v
->breakdown_chance
= 0;
1292 * Handle all of the aspects of a vehicle breakdown
1293 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1294 * @return true iff the vehicle is stopped because of a breakdown
1295 * @note This function always returns false for aircraft, since these never stop for breakdowns
1297 bool Vehicle::HandleBreakdown()
1299 /* Possible states for Vehicle::breakdown_ctr
1300 * 0 - vehicle is running normally
1301 * 1 - vehicle is currently broken down
1302 * 2 - vehicle is going to break down now
1303 * >2 - vehicle is counting down to the actual breakdown event */
1304 switch (this->breakdown_ctr
) {
1309 this->breakdown_ctr
= 1;
1311 if (this->breakdowns_since_last_service
!= 255) {
1312 this->breakdowns_since_last_service
++;
1315 if (this->type
== VEH_AIRCRAFT
) {
1316 /* Aircraft just need this flag, the rest is handled elsewhere */
1317 this->vehstatus
|= VS_AIRCRAFT_BROKEN
;
1319 this->cur_speed
= 0;
1321 if (!PlayVehicleSound(this, VSE_BREAKDOWN
)) {
1322 bool train_or_ship
= this->type
== VEH_TRAIN
|| this->type
== VEH_SHIP
;
1323 SndPlayVehicleFx((_settings_game
.game_creation
.landscape
!= LT_TOYLAND
) ?
1324 (train_or_ship
? SND_10_BREAKDOWN_TRAIN_SHIP
: SND_0F_BREAKDOWN_ROADVEHICLE
) :
1325 (train_or_ship
? SND_3A_BREAKDOWN_TRAIN_SHIP_TOYLAND
: SND_35_BREAKDOWN_ROADVEHICLE_TOYLAND
), this);
1328 if (!(this->vehstatus
& VS_HIDDEN
) && !HasBit(EngInfo(this->engine_type
)->misc_flags
, EF_NO_BREAKDOWN_SMOKE
)) {
1329 EffectVehicle
*u
= CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE
);
1330 if (u
!= nullptr) u
->animation_state
= this->breakdown_delay
* 2;
1334 this->MarkDirty(); // Update graphics after speed is zeroed
1335 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1336 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
1340 /* Aircraft breakdowns end only when arriving at the airport */
1341 if (this->type
== VEH_AIRCRAFT
) return false;
1343 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1344 if ((this->tick_counter
& (this->type
== VEH_TRAIN
? 3 : 1)) == 0) {
1345 if (--this->breakdown_delay
== 0) {
1346 this->breakdown_ctr
= 0;
1348 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1354 if (!this->current_order
.IsType(OT_LOADING
)) this->breakdown_ctr
--;
1360 * Update age of a vehicle.
1361 * @param v Vehicle to update.
1363 void AgeVehicle(Vehicle
*v
)
1365 if (v
->age
< MAX_DAY
) {
1367 if (v
->IsPrimaryVehicle() && v
->age
== VEHICLE_PROFIT_MIN_AGE
+ 1) GroupStatistics::VehicleReachedProfitAge(v
);
1370 if (!v
->IsPrimaryVehicle() && (v
->type
!= VEH_TRAIN
|| !Train::From(v
)->IsEngine())) return;
1372 int age
= v
->age
- v
->max_age
;
1373 if (age
== DAYS_IN_LEAP_YEAR
* 0 || age
== DAYS_IN_LEAP_YEAR
* 1 ||
1374 age
== DAYS_IN_LEAP_YEAR
* 2 || age
== DAYS_IN_LEAP_YEAR
* 3 || age
== DAYS_IN_LEAP_YEAR
* 4) {
1375 v
->reliability_spd_dec
<<= 1;
1378 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1380 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1381 if (v
->Previous() != nullptr || v
->owner
!= _local_company
|| (v
->vehstatus
& VS_CRASHED
) != 0) return;
1383 const Company
*c
= Company::Get(v
->owner
);
1384 /* Don't warn if a renew is active */
1385 if (c
->settings
.engine_renew
&& v
->GetEngine()->company_avail
!= 0) return;
1386 /* Don't warn if a replacement is active */
1387 if (EngineHasReplacementForCompany(c
, v
->engine_type
, v
->group_id
)) return;
1390 if (age
== -DAYS_IN_LEAP_YEAR
) {
1391 str
= STR_NEWS_VEHICLE_IS_GETTING_OLD
;
1392 } else if (age
== 0) {
1393 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD
;
1394 } else if (age
> 0 && (age
% DAYS_IN_LEAP_YEAR
) == 0) {
1395 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND
;
1400 SetDParam(0, v
->index
);
1401 AddVehicleAdviceNewsItem(str
, v
->index
);
1405 * Calculates how full a vehicle is.
1406 * @param front The front vehicle of the consist to check.
1407 * @param colour The string to show depending on if we are unloading or loading
1408 * @return A percentage of how full the Vehicle is.
1409 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1410 * if the vehicle is completely empty or full.
1411 * This is useful for both display and conditional orders.
1413 uint8
CalcPercentVehicleFilled(const Vehicle
*front
, StringID
*colour
)
1419 bool loading
= false;
1421 bool is_loading
= front
->current_order
.IsType(OT_LOADING
);
1423 /* The station may be nullptr when the (colour) string does not need to be set. */
1424 const Station
*st
= Station::GetIfValid(front
->last_station_visited
);
1425 assert(colour
== nullptr || (st
!= nullptr && is_loading
));
1427 bool order_no_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_NO_LOAD
);
1428 bool order_full_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_FULL_LOAD
);
1430 /* Count up max and used */
1431 for (const Vehicle
*v
= front
; v
!= nullptr; v
= v
->Next()) {
1432 count
+= v
->cargo
.StoredCount();
1433 max
+= v
->cargo_cap
;
1434 if (v
->cargo_cap
!= 0 && colour
!= nullptr) {
1435 unloading
+= HasBit(v
->vehicle_flags
, VF_CARGO_UNLOADING
) ? 1 : 0;
1436 loading
|= !order_no_load
&&
1437 (order_full_load
|| st
->goods
[v
->cargo_type
].HasRating()) &&
1438 !HasBit(v
->vehicle_flags
, VF_LOADING_FINISHED
) && !HasBit(v
->vehicle_flags
, VF_STOP_LOADING
);
1443 if (colour
!= nullptr) {
1444 if (unloading
== 0 && loading
) {
1445 *colour
= STR_PERCENT_UP
;
1446 } else if (unloading
== 0 && !loading
) {
1447 *colour
= STR_PERCENT_NONE
;
1448 } else if (cars
== unloading
|| !loading
) {
1449 *colour
= STR_PERCENT_DOWN
;
1451 *colour
= STR_PERCENT_UP_DOWN
;
1455 /* Train without capacity */
1456 if (max
== 0) return 100;
1458 /* Return the percentage */
1459 if (count
* 2 < max
) {
1460 /* Less than 50%; round up, so that 0% means really empty. */
1461 return CeilDiv(count
* 100, max
);
1463 /* More than 50%; round down, so that 100% means really full. */
1464 return (count
* 100) / max
;
1469 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1470 * @param v Vehicle that entered a depot.
1472 void VehicleEnterDepot(Vehicle
*v
)
1474 /* Always work with the front of the vehicle */
1475 assert(v
== v
->First());
1479 Train
*t
= Train::From(v
);
1480 SetWindowClassesDirty(WC_TRAINS_LIST
);
1481 /* Clear path reservation */
1482 SetDepotReservation(t
->tile
, false);
1483 if (_settings_client
.gui
.show_track_reservation
) MarkTileDirtyByTile(t
->tile
);
1485 UpdateSignalsOnSegment(t
->tile
, INVALID_DIAGDIR
, t
->owner
);
1486 t
->wait_counter
= 0;
1487 t
->force_proceed
= TFP_NONE
;
1488 ClrBit(t
->flags
, VRF_TOGGLE_REVERSE
);
1489 t
->ConsistChanged(CCF_ARRANGE
);
1494 SetWindowClassesDirty(WC_ROADVEH_LIST
);
1498 SetWindowClassesDirty(WC_SHIPS_LIST
);
1499 Ship
*ship
= Ship::From(v
);
1500 ship
->state
= TRACK_BIT_DEPOT
;
1501 ship
->UpdateCache();
1502 ship
->UpdateViewport(true, true);
1503 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1508 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
1509 HandleAircraftEnterHangar(Aircraft::From(v
));
1511 default: NOT_REACHED();
1513 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1515 if (v
->type
!= VEH_TRAIN
) {
1516 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1517 * We only increase the number of vehicles when the first one enters, so we will not need to search for more vehicles in the depot */
1518 InvalidateWindowData(WC_VEHICLE_DEPOT
, v
->tile
);
1520 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1522 v
->vehstatus
|= VS_HIDDEN
;
1525 VehicleServiceInDepot(v
);
1527 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1528 TriggerVehicle(v
, VEHICLE_TRIGGER_DEPOT
);
1531 InvalidateWindowData(WC_VEHICLE_VIEW
, v
->index
);
1533 if (v
->current_order
.IsType(OT_GOTO_DEPOT
)) {
1534 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1536 const Order
*real_order
= v
->GetOrder(v
->cur_real_order_index
);
1538 /* Test whether we are heading for this depot. If not, do nothing.
1539 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1540 if ((v
->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) &&
1541 real_order
!= nullptr && !(real_order
->GetDepotActionType() & ODATFB_NEAREST_DEPOT
) &&
1542 (v
->type
== VEH_AIRCRAFT
? v
->current_order
.GetDestination() != GetStationIndex(v
->tile
) : v
->dest_tile
!= v
->tile
)) {
1543 /* We are heading for another depot, keep driving. */
1547 if (v
->current_order
.IsRefit()) {
1548 Backup
<CompanyID
> cur_company(_current_company
, v
->owner
, FILE_LINE
);
1549 CommandCost cost
= DoCommand(v
->tile
, v
->index
, v
->current_order
.GetRefitCargo() | 0xFF << 8, DC_EXEC
, GetCmdRefitVeh(v
));
1550 cur_company
.Restore();
1552 if (cost
.Failed()) {
1553 _vehicles_to_autoreplace
[v
] = false;
1554 if (v
->owner
== _local_company
) {
1555 /* Notify the user that we stopped the vehicle */
1556 SetDParam(0, v
->index
);
1557 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED
, v
->index
);
1559 } else if (cost
.GetCost() != 0) {
1560 v
->profit_this_year
-= cost
.GetCost() << 8;
1561 if (v
->owner
== _local_company
) {
1562 ShowCostOrIncomeAnimation(v
->x_pos
, v
->y_pos
, v
->z_pos
, cost
.GetCost());
1567 if (v
->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) {
1568 /* Part of orders */
1569 v
->DeleteUnreachedImplicitOrders();
1570 UpdateVehicleTimetable(v
, true);
1571 v
->IncrementImplicitOrderIndex();
1573 if (v
->current_order
.GetDepotActionType() & ODATFB_HALT
) {
1574 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1575 _vehicles_to_autoreplace
[v
] = false;
1576 /* Invalidate last_loading_station. As the link from the station
1577 * before the stop to the station after the stop can't be predicted
1578 * we shouldn't construct it when the vehicle visits the next stop. */
1579 v
->last_loading_station
= INVALID_STATION
;
1580 if (v
->owner
== _local_company
) {
1581 SetDParam(0, v
->index
);
1582 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING
+ v
->type
, v
->index
);
1584 AI::NewEvent(v
->owner
, new ScriptEventVehicleWaitingInDepot(v
->index
));
1586 v
->current_order
.MakeDummy();
1592 * Update the position of the vehicle. This will update the hash that tells
1593 * which vehicles are on a tile.
1595 void Vehicle::UpdatePosition()
1597 UpdateVehicleTileHash(this, false);
1601 * Update the bounding box co-ordinates of the vehicle
1602 * @param update_cache Update the cached values for previous co-ordinate values
1604 void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache
) const
1607 this->sprite_cache
.sprite_seq
.GetBounds(&new_coord
);
1609 Point pt
= RemapCoords(this->x_pos
+ this->x_offs
, this->y_pos
+ this->y_offs
, this->z_pos
);
1610 new_coord
.left
+= pt
.x
;
1611 new_coord
.top
+= pt
.y
;
1612 new_coord
.right
+= pt
.x
+ 2 * ZOOM_LVL_BASE
;
1613 new_coord
.bottom
+= pt
.y
+ 2 * ZOOM_LVL_BASE
;
1617 * If the old coordinates are invalid, set the cache to the new coordinates for correct
1618 * behaviour the next time the coordinate cache is checked.
1620 this->sprite_cache
.old_coord
= this->coord
.left
== INVALID_COORD
? new_coord
: this->coord
;
1623 /* Extend the bounds of the existing cached bounding box so the next dirty window is correct */
1624 this->sprite_cache
.old_coord
.left
= std::min(this->sprite_cache
.old_coord
.left
, this->coord
.left
);
1625 this->sprite_cache
.old_coord
.top
= std::min(this->sprite_cache
.old_coord
.top
, this->coord
.top
);
1626 this->sprite_cache
.old_coord
.right
= std::max(this->sprite_cache
.old_coord
.right
, this->coord
.right
);
1627 this->sprite_cache
.old_coord
.bottom
= std::max(this->sprite_cache
.old_coord
.bottom
, this->coord
.bottom
);
1630 this->coord
= new_coord
;
1634 * Update the vehicle on the viewport, updating the right hash and setting the new coordinates.
1635 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1637 void Vehicle::UpdateViewport(bool dirty
)
1639 Rect old_coord
= this->sprite_cache
.old_coord
;
1641 this->UpdateBoundingBoxCoordinates(true);
1642 UpdateVehicleViewportHash(this, this->coord
.left
, this->coord
.top
, old_coord
.left
, old_coord
.top
);
1645 if (old_coord
.left
== INVALID_COORD
) {
1646 this->sprite_cache
.is_viewport_candidate
= this->MarkAllViewportsDirty();
1648 this->sprite_cache
.is_viewport_candidate
= ::MarkAllViewportsDirty(
1649 std::min(this->sprite_cache
.old_coord
.left
, this->coord
.left
),
1650 std::min(this->sprite_cache
.old_coord
.top
, this->coord
.top
),
1651 std::max(this->sprite_cache
.old_coord
.right
, this->coord
.right
),
1652 std::max(this->sprite_cache
.old_coord
.bottom
, this->coord
.bottom
));
1658 * Update the position of the vehicle, and update the viewport.
1660 void Vehicle::UpdatePositionAndViewport()
1662 this->UpdatePosition();
1663 this->UpdateViewport(true);
1667 * Marks viewports dirty where the vehicle's image is.
1668 * @return true if at least one viewport has a dirty block
1670 bool Vehicle::MarkAllViewportsDirty() const
1672 return ::MarkAllViewportsDirty(this->coord
.left
, this->coord
.top
, this->coord
.right
, this->coord
.bottom
);
1676 * Get position information of a vehicle when moving one pixel in the direction it is facing
1677 * @param v Vehicle to move
1678 * @return Position information after the move
1680 GetNewVehiclePosResult
GetNewVehiclePos(const Vehicle
*v
)
1682 static const int8 _delta_coord
[16] = {
1683 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1684 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1687 int x
= v
->x_pos
+ _delta_coord
[v
->direction
];
1688 int y
= v
->y_pos
+ _delta_coord
[v
->direction
+ 8];
1690 GetNewVehiclePosResult gp
;
1693 gp
.old_tile
= v
->tile
;
1694 gp
.new_tile
= TileVirtXY(x
, y
);
1698 static const Direction _new_direction_table
[] = {
1699 DIR_N
, DIR_NW
, DIR_W
,
1700 DIR_NE
, DIR_SE
, DIR_SW
,
1701 DIR_E
, DIR_SE
, DIR_S
1704 Direction
GetDirectionTowards(const Vehicle
*v
, int x
, int y
)
1708 if (y
>= v
->y_pos
) {
1709 if (y
!= v
->y_pos
) i
+= 3;
1713 if (x
>= v
->x_pos
) {
1714 if (x
!= v
->x_pos
) i
++;
1718 Direction dir
= v
->direction
;
1720 DirDiff dirdiff
= DirDifference(_new_direction_table
[i
], dir
);
1721 if (dirdiff
== DIRDIFF_SAME
) return dir
;
1722 return ChangeDir(dir
, dirdiff
> DIRDIFF_REVERSE
? DIRDIFF_45LEFT
: DIRDIFF_45RIGHT
);
1726 * Call the tile callback function for a vehicle entering a tile
1727 * @param v Vehicle entering the tile
1728 * @param tile Tile entered
1729 * @param x X position
1730 * @param y Y position
1731 * @return Some meta-data over the to be entered tile.
1732 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1734 VehicleEnterTileStatus
VehicleEnterTile(Vehicle
*v
, TileIndex tile
, int x
, int y
)
1736 return _tile_type_procs
[GetTileType(tile
)]->vehicle_enter_tile_proc(v
, tile
, x
, y
);
1740 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1741 * struct initialization, except after each call to this->NextID() the returned value
1742 * is assigned to a vehicle.
1743 * @param type type of vehicle
1744 * @param owner owner of vehicles
1746 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type
, CompanyID owner
) : cache(nullptr), maxid(0), curid(0)
1749 for (const Vehicle
*v
: Vehicle::Iterate()) {
1750 if (v
->type
== type
&& v
->owner
== owner
) {
1751 this->maxid
= std::max
<UnitID
>(this->maxid
, v
->unitnumber
);
1755 if (this->maxid
== 0) return;
1757 /* Reserving 'maxid + 2' because we need:
1758 * - space for the last item (with v->unitnumber == maxid)
1759 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1760 this->cache
= CallocT
<bool>(this->maxid
+ 2);
1762 /* Fill the cache */
1763 for (const Vehicle
*v
: Vehicle::Iterate()) {
1764 if (v
->type
== type
&& v
->owner
== owner
) {
1765 this->cache
[v
->unitnumber
] = true;
1770 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1771 UnitID
FreeUnitIDGenerator::NextID()
1773 if (this->maxid
<= this->curid
) return ++this->curid
;
1775 while (this->cache
[++this->curid
]) { } // it will stop, we reserved more space than needed
1781 * Get an unused unit number for a vehicle (if allowed).
1782 * @param type Type of vehicle
1783 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1785 UnitID
GetFreeUnitNumber(VehicleType type
)
1787 /* Check whether it is allowed to build another vehicle. */
1790 case VEH_TRAIN
: max_veh
= _settings_game
.vehicle
.max_trains
; break;
1791 case VEH_ROAD
: max_veh
= _settings_game
.vehicle
.max_roadveh
; break;
1792 case VEH_SHIP
: max_veh
= _settings_game
.vehicle
.max_ships
; break;
1793 case VEH_AIRCRAFT
: max_veh
= _settings_game
.vehicle
.max_aircraft
; break;
1794 default: NOT_REACHED();
1797 const Company
*c
= Company::Get(_current_company
);
1798 if (c
->group_all
[type
].num_vehicle
>= max_veh
) return UINT16_MAX
; // Currently already at the limit, no room to make a new one.
1800 FreeUnitIDGenerator
gen(type
, _current_company
);
1802 return gen
.NextID();
1807 * Check whether we can build infrastructure for the given
1808 * vehicle type. This to disable building stations etc. when
1809 * you are not allowed/able to have the vehicle type yet.
1810 * @param type the vehicle type to check this for
1811 * @return true if there is any reason why you may build
1812 * the infrastructure for the given vehicle type
1814 bool CanBuildVehicleInfrastructure(VehicleType type
, byte subtype
)
1816 assert(IsCompanyBuildableVehicleType(type
));
1818 if (!Company::IsValidID(_local_company
)) return false;
1823 if (!HasAnyRailtypesAvail(_local_company
)) return false;
1824 max
= _settings_game
.vehicle
.max_trains
;
1827 if (!HasAnyRoadTypesAvail(_local_company
, (RoadTramType
)subtype
)) return false;
1828 max
= _settings_game
.vehicle
.max_roadveh
;
1830 case VEH_SHIP
: max
= _settings_game
.vehicle
.max_ships
; break;
1831 case VEH_AIRCRAFT
: max
= _settings_game
.vehicle
.max_aircraft
; break;
1832 default: NOT_REACHED();
1835 /* We can build vehicle infrastructure when we may build the vehicle type */
1837 /* Can we actually build the vehicle type? */
1838 for (const Engine
*e
: Engine::IterateType(type
)) {
1839 if (type
== VEH_ROAD
&& GetRoadTramType(e
->u
.road
.roadtype
) != (RoadTramType
)subtype
) continue;
1840 if (HasBit(e
->company_avail
, _local_company
)) return true;
1845 /* We should be able to build infrastructure when we have the actual vehicle type */
1846 for (const Vehicle
*v
: Vehicle::Iterate()) {
1847 if (v
->type
== VEH_ROAD
&& GetRoadTramType(RoadVehicle::From(v
)->roadtype
) != (RoadTramType
)subtype
) continue;
1848 if (v
->owner
== _local_company
&& v
->type
== type
) return true;
1856 * Determines the #LiveryScheme for a vehicle.
1857 * @param engine_type Engine of the vehicle.
1858 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1859 * @param v the vehicle, \c nullptr if in purchase list etc.
1860 * @return livery scheme to use.
1862 LiveryScheme
GetEngineLiveryScheme(EngineID engine_type
, EngineID parent_engine_type
, const Vehicle
*v
)
1864 CargoID cargo_type
= v
== nullptr ? (CargoID
)CT_INVALID
: v
->cargo_type
;
1865 const Engine
*e
= Engine::Get(engine_type
);
1867 default: NOT_REACHED();
1869 if (v
!= nullptr && parent_engine_type
!= INVALID_ENGINE
&& (UsesWagonOverride(v
) || (v
->IsArticulatedPart() && e
->u
.rail
.railveh_type
!= RAILVEH_WAGON
))) {
1870 /* Wagonoverrides use the colour scheme of the front engine.
1871 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1872 engine_type
= parent_engine_type
;
1873 e
= Engine::Get(engine_type
);
1874 /* Note: Luckily cargo_type is not needed for engines */
1877 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1878 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1879 if (e
->u
.rail
.railveh_type
== RAILVEH_WAGON
) {
1880 if (!CargoSpec::Get(cargo_type
)->is_freight
) {
1881 if (parent_engine_type
== INVALID_ENGINE
) {
1882 return LS_PASSENGER_WAGON_STEAM
;
1884 bool is_mu
= HasBit(EngInfo(parent_engine_type
)->misc_flags
, EF_RAIL_IS_MU
);
1885 switch (RailVehInfo(parent_engine_type
)->engclass
) {
1886 default: NOT_REACHED();
1887 case EC_STEAM
: return LS_PASSENGER_WAGON_STEAM
;
1888 case EC_DIESEL
: return is_mu
? LS_DMU
: LS_PASSENGER_WAGON_DIESEL
;
1889 case EC_ELECTRIC
: return is_mu
? LS_EMU
: LS_PASSENGER_WAGON_ELECTRIC
;
1890 case EC_MONORAIL
: return LS_PASSENGER_WAGON_MONORAIL
;
1891 case EC_MAGLEV
: return LS_PASSENGER_WAGON_MAGLEV
;
1895 return LS_FREIGHT_WAGON
;
1898 bool is_mu
= HasBit(e
->info
.misc_flags
, EF_RAIL_IS_MU
);
1900 switch (e
->u
.rail
.engclass
) {
1901 default: NOT_REACHED();
1902 case EC_STEAM
: return LS_STEAM
;
1903 case EC_DIESEL
: return is_mu
? LS_DMU
: LS_DIESEL
;
1904 case EC_ELECTRIC
: return is_mu
? LS_EMU
: LS_ELECTRIC
;
1905 case EC_MONORAIL
: return LS_MONORAIL
;
1906 case EC_MAGLEV
: return LS_MAGLEV
;
1911 /* Always use the livery of the front */
1912 if (v
!= nullptr && parent_engine_type
!= INVALID_ENGINE
) {
1913 engine_type
= parent_engine_type
;
1914 e
= Engine::Get(engine_type
);
1915 cargo_type
= v
->First()->cargo_type
;
1917 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1918 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1920 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1921 if (HasBit(e
->info
.misc_flags
, EF_ROAD_TRAM
)) {
1923 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_TRAM
: LS_FREIGHT_TRAM
;
1926 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_BUS
: LS_TRUCK
;
1930 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1931 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1932 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_SHIP
: LS_FREIGHT_SHIP
;
1935 switch (e
->u
.air
.subtype
) {
1936 case AIR_HELI
: return LS_HELICOPTER
;
1937 case AIR_CTOL
: return LS_SMALL_PLANE
;
1938 case AIR_CTOL
| AIR_FAST
: return LS_LARGE_PLANE
;
1939 default: NOT_REACHED();
1945 * Determines the livery for a vehicle.
1946 * @param engine_type EngineID of the vehicle
1947 * @param company Owner of the vehicle
1948 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1949 * @param v the vehicle. nullptr if in purchase list etc.
1950 * @param livery_setting The livery settings to use for acquiring the livery information.
1951 * @return livery to use
1953 const Livery
*GetEngineLivery(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
, byte livery_setting
)
1955 const Company
*c
= Company::Get(company
);
1956 LiveryScheme scheme
= LS_DEFAULT
;
1958 if (livery_setting
== LIT_ALL
|| (livery_setting
== LIT_COMPANY
&& company
== _local_company
)) {
1960 const Group
*g
= Group::GetIfValid(v
->First()->group_id
);
1962 /* Traverse parents until we find a livery or reach the top */
1963 while (g
->livery
.in_use
== 0 && g
->parent
!= INVALID_GROUP
) {
1964 g
= Group::Get(g
->parent
);
1966 if (g
->livery
.in_use
!= 0) return &g
->livery
;
1970 /* The default livery is always available for use, but its in_use flag determines
1971 * whether any _other_ liveries are in use. */
1972 if (c
->livery
[LS_DEFAULT
].in_use
!= 0) {
1973 /* Determine the livery scheme to use */
1974 scheme
= GetEngineLiveryScheme(engine_type
, parent_engine_type
, v
);
1978 return &c
->livery
[scheme
];
1982 static PaletteID
GetEngineColourMap(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
)
1984 PaletteID map
= (v
!= nullptr) ? v
->colourmap
: PAL_NONE
;
1986 /* Return cached value if any */
1987 if (map
!= PAL_NONE
) return map
;
1989 const Engine
*e
= Engine::Get(engine_type
);
1991 /* Check if we should use the colour map callback */
1992 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_COLOUR_REMAP
)) {
1993 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING
, 0, 0, engine_type
, v
);
1994 /* Failure means "use the default two-colour" */
1995 if (callback
!= CALLBACK_FAILED
) {
1996 static_assert(PAL_NONE
== 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1997 map
= GB(callback
, 0, 14);
1998 /* If bit 14 is set, then the company colours are applied to the
1999 * map else it's returned as-is. */
2000 if (!HasBit(callback
, 14)) {
2002 if (v
!= nullptr) const_cast<Vehicle
*>(v
)->colourmap
= map
;
2008 bool twocc
= HasBit(e
->info
.misc_flags
, EF_USES_2CC
);
2010 if (map
== PAL_NONE
) map
= twocc
? (PaletteID
)SPR_2CCMAP_BASE
: (PaletteID
)PALETTE_RECOLOUR_START
;
2012 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2013 if (!Company::IsValidID(company
)) return map
;
2015 const Livery
*livery
= GetEngineLivery(engine_type
, company
, parent_engine_type
, v
, _settings_client
.gui
.liveries
);
2017 map
+= livery
->colour1
;
2018 if (twocc
) map
+= livery
->colour2
* 16;
2021 if (v
!= nullptr) const_cast<Vehicle
*>(v
)->colourmap
= map
;
2026 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2027 * @param engine_type ID of engine
2028 * @param company ID of company
2029 * @return A ready-to-use palette modifier
2031 PaletteID
GetEnginePalette(EngineID engine_type
, CompanyID company
)
2033 return GetEngineColourMap(engine_type
, company
, INVALID_ENGINE
, nullptr);
2037 * Get the colour map for a vehicle.
2038 * @param v Vehicle to get colour map for
2039 * @return A ready-to-use palette modifier
2041 PaletteID
GetVehiclePalette(const Vehicle
*v
)
2043 if (v
->IsGroundVehicle()) {
2044 return GetEngineColourMap(v
->engine_type
, v
->owner
, v
->GetGroundVehicleCache()->first_engine
, v
);
2047 return GetEngineColourMap(v
->engine_type
, v
->owner
, INVALID_ENGINE
, v
);
2051 * Delete all implicit orders which were not reached.
2053 void Vehicle::DeleteUnreachedImplicitOrders()
2055 if (this->IsGroundVehicle()) {
2056 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2057 if (HasBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
)) {
2058 /* Do not delete orders, only skip them */
2059 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2060 this->cur_implicit_order_index
= this->cur_real_order_index
;
2061 InvalidateVehicleOrder(this, 0);
2066 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2067 while (order
!= nullptr) {
2068 if (this->cur_implicit_order_index
== this->cur_real_order_index
) break;
2070 if (order
->IsType(OT_IMPLICIT
)) {
2071 DeleteOrder(this, this->cur_implicit_order_index
);
2072 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2073 order
= this->GetOrder(this->cur_implicit_order_index
);
2075 /* Skip non-implicit orders, e.g. service-orders */
2076 order
= order
->next
;
2077 this->cur_implicit_order_index
++;
2081 if (order
== nullptr) {
2082 order
= this->GetOrder(0);
2083 this->cur_implicit_order_index
= 0;
2089 * Prepare everything to begin the loading when arriving at a station.
2090 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2092 void Vehicle::BeginLoading()
2094 assert(IsTileType(this->tile
, MP_STATION
) || this->type
== VEH_SHIP
);
2096 if (this->current_order
.IsType(OT_GOTO_STATION
) &&
2097 this->current_order
.GetDestination() == this->last_station_visited
) {
2098 this->DeleteUnreachedImplicitOrders();
2100 /* Now both order indices point to the destination station, and we can start loading */
2101 this->current_order
.MakeLoading(true);
2102 UpdateVehicleTimetable(this, true);
2104 /* Furthermore add the Non Stop flag to mark that this station
2105 * is the actual destination of the vehicle, which is (for example)
2106 * necessary to be known for HandleTrainLoading to determine
2107 * whether the train is lost or not; not marking a train lost
2108 * that arrives at random stations is bad. */
2109 this->current_order
.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION
);
2112 /* We weren't scheduled to stop here. Insert an implicit order
2113 * to show that we are stopping here.
2114 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2115 * the 'wrong' terminal when skipping orders etc. */
2116 Order
*in_list
= this->GetOrder(this->cur_implicit_order_index
);
2117 if (this->IsGroundVehicle() &&
2118 (in_list
== nullptr || !in_list
->IsType(OT_IMPLICIT
) ||
2119 in_list
->GetDestination() != this->last_station_visited
)) {
2120 bool suppress_implicit_orders
= HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS
);
2121 /* Do not create consecutive duplicates of implicit orders */
2122 Order
*prev_order
= this->cur_implicit_order_index
> 0 ? this->GetOrder(this->cur_implicit_order_index
- 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2123 if (prev_order
== nullptr ||
2124 (!prev_order
->IsType(OT_IMPLICIT
) && !prev_order
->IsType(OT_GOTO_STATION
)) ||
2125 prev_order
->GetDestination() != this->last_station_visited
) {
2127 /* Prefer deleting implicit orders instead of inserting new ones,
2128 * so test whether the right order follows later. In case of only
2129 * implicit orders treat the last order in the list like an
2130 * explicit one, except if the overall number of orders surpasses
2131 * IMPLICIT_ORDER_ONLY_CAP. */
2132 int target_index
= this->cur_implicit_order_index
;
2134 while (target_index
!= this->cur_real_order_index
|| this->GetNumManualOrders() == 0) {
2135 const Order
*order
= this->GetOrder(target_index
);
2136 if (order
== nullptr) break; // No orders.
2137 if (order
->IsType(OT_IMPLICIT
) && order
->GetDestination() == this->last_station_visited
) {
2142 if (target_index
>= this->orders
.list
->GetNumOrders()) {
2143 if (this->GetNumManualOrders() == 0 &&
2144 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP
) {
2149 if (target_index
== this->cur_implicit_order_index
) break; // Avoid infinite loop.
2153 if (suppress_implicit_orders
) {
2154 /* Skip to the found order */
2155 this->cur_implicit_order_index
= target_index
;
2156 InvalidateVehicleOrder(this, 0);
2158 /* Delete all implicit orders up to the station we just reached */
2159 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2160 while (!order
->IsType(OT_IMPLICIT
) || order
->GetDestination() != this->last_station_visited
) {
2161 if (order
->IsType(OT_IMPLICIT
)) {
2162 DeleteOrder(this, this->cur_implicit_order_index
);
2163 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2164 order
= this->GetOrder(this->cur_implicit_order_index
);
2166 /* Skip non-implicit orders, e.g. service-orders */
2167 order
= order
->next
;
2168 this->cur_implicit_order_index
++;
2172 if (order
== nullptr) {
2173 order
= this->GetOrder(0);
2174 this->cur_implicit_order_index
= 0;
2176 assert(order
!= nullptr);
2179 } else if (!suppress_implicit_orders
&&
2180 ((this->orders
.list
== nullptr ? OrderList::CanAllocateItem() : this->orders
.list
->GetNumOrders() < MAX_VEH_ORDER_ID
)) &&
2181 Order::CanAllocateItem()) {
2182 /* Insert new implicit order */
2183 Order
*implicit_order
= new Order();
2184 implicit_order
->MakeImplicit(this->last_station_visited
);
2185 InsertOrder(this, implicit_order
, this->cur_implicit_order_index
);
2186 if (this->cur_implicit_order_index
> 0) --this->cur_implicit_order_index
;
2188 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2189 * Reenable it for this vehicle */
2190 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2191 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2195 this->current_order
.MakeLoading(false);
2198 if (this->last_loading_station
!= INVALID_STATION
&&
2199 this->last_loading_station
!= this->last_station_visited
&&
2200 ((this->current_order
.GetLoadType() & OLFB_NO_LOAD
) == 0 ||
2201 (this->current_order
.GetUnloadType() & OUFB_NO_UNLOAD
) == 0)) {
2202 IncreaseStats(Station::Get(this->last_loading_station
), this, this->last_station_visited
);
2205 PrepareUnload(this);
2207 SetWindowDirty(GetWindowClassForVehicleType(this->type
), this->owner
);
2208 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2209 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
2210 SetWindowDirty(WC_STATION_VIEW
, this->last_station_visited
);
2212 Station::Get(this->last_station_visited
)->MarkTilesDirty(true);
2213 this->cur_speed
= 0;
2218 * Return all reserved cargo packets to the station and reset all packets
2219 * staged for transfer.
2220 * @param st the station where the reserved packets should go.
2222 void Vehicle::CancelReservation(StationID next
, Station
*st
)
2224 for (Vehicle
*v
= this; v
!= nullptr; v
= v
->next
) {
2225 VehicleCargoList
&cargo
= v
->cargo
;
2226 if (cargo
.ActionCount(VehicleCargoList::MTA_LOAD
) > 0) {
2227 DEBUG(misc
, 1, "cancelling cargo reservation");
2228 cargo
.Return(UINT_MAX
, &st
->goods
[v
->cargo_type
].cargo
, next
);
2229 cargo
.SetTransferLoadPlace(st
->xy
);
2236 * Perform all actions when leaving a station.
2237 * @pre this->current_order.IsType(OT_LOADING)
2239 void Vehicle::LeaveStation()
2241 assert(this->current_order
.IsType(OT_LOADING
));
2243 delete this->cargo_payment
;
2244 assert(this->cargo_payment
== nullptr); // cleared by ~CargoPayment
2246 /* Only update the timetable if the vehicle was supposed to stop here. */
2247 if (this->current_order
.GetNonStopType() != ONSF_STOP_EVERYWHERE
) UpdateVehicleTimetable(this, false);
2249 if ((this->current_order
.GetLoadType() & OLFB_NO_LOAD
) == 0 ||
2250 (this->current_order
.GetUnloadType() & OUFB_NO_UNLOAD
) == 0) {
2251 if (this->current_order
.CanLeaveWithCargo(this->last_loading_station
!= INVALID_STATION
)) {
2252 /* Refresh next hop stats to make sure we've done that at least once
2253 * during the stop and that refit_cap == cargo_cap for each vehicle in
2255 this->ResetRefitCaps();
2256 LinkRefresher::Run(this);
2258 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2259 this->last_loading_station
= this->last_station_visited
;
2261 /* if the vehicle couldn't load and had to unload or transfer everything
2262 * set the last loading station to invalid as it will leave empty. */
2263 this->last_loading_station
= INVALID_STATION
;
2267 this->current_order
.MakeLeaveStation();
2268 Station
*st
= Station::Get(this->last_station_visited
);
2269 this->CancelReservation(INVALID_STATION
, st
);
2270 st
->loading_vehicles
.remove(this);
2272 HideFillingPercent(&this->fill_percent_te_id
);
2273 trip_occupancy
= CalcPercentVehicleFilled(this, nullptr);
2275 if (this->type
== VEH_TRAIN
&& !(this->vehstatus
& VS_CRASHED
)) {
2276 /* Trigger station animation (trains only) */
2277 if (IsTileType(this->tile
, MP_STATION
)) {
2278 TriggerStationRandomisation(st
, this->tile
, SRT_TRAIN_DEPARTS
);
2279 TriggerStationAnimation(st
, this->tile
, SAT_TRAIN_DEPARTS
);
2282 SetBit(Train::From(this)->flags
, VRF_LEAVING_STATION
);
2289 * Reset all refit_cap in the consist to cargo_cap.
2291 void Vehicle::ResetRefitCaps()
2293 for (Vehicle
*v
= this; v
!= nullptr; v
= v
->Next()) v
->refit_cap
= v
->cargo_cap
;
2297 * Handle the loading of the vehicle; when not it skips through dummy
2298 * orders and does nothing in all other cases.
2299 * @param mode is the non-first call for this vehicle in this tick?
2301 void Vehicle::HandleLoading(bool mode
)
2303 switch (this->current_order
.GetType()) {
2305 uint wait_time
= std::max(this->current_order
.GetTimetabledWait() - this->lateness_counter
, 0);
2307 /* Not the first call for this tick, or still loading */
2308 if (mode
|| !HasBit(this->vehicle_flags
, VF_LOADING_FINISHED
) || this->current_order_time
< wait_time
) return;
2310 this->PlayLeaveStationSound();
2312 this->LeaveStation();
2314 /* Only advance to next order if we just loaded at the current one */
2315 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2316 if (order
== nullptr ||
2317 (!order
->IsType(OT_IMPLICIT
) && !order
->IsType(OT_GOTO_STATION
)) ||
2318 order
->GetDestination() != this->last_station_visited
) {
2324 case OT_DUMMY
: break;
2329 this->IncrementImplicitOrderIndex();
2333 * Get a map of cargoes and free capacities in the consist.
2334 * @param capacities Map to be filled with cargoes and capacities.
2336 void Vehicle::GetConsistFreeCapacities(SmallMap
<CargoID
, uint
> &capacities
) const
2338 for (const Vehicle
*v
= this; v
!= nullptr; v
= v
->Next()) {
2339 if (v
->cargo_cap
== 0) continue;
2340 std::pair
<CargoID
, uint
> *pair
= capacities
.Find(v
->cargo_type
);
2341 if (pair
== capacities
.End()) {
2342 capacities
.push_back({v
->cargo_type
, v
->cargo_cap
- v
->cargo
.StoredCount()});
2344 pair
->second
+= v
->cargo_cap
- v
->cargo
.StoredCount();
2349 uint
Vehicle::GetConsistTotalCapacity() const
2352 for (const Vehicle
*v
= this; v
!= nullptr; v
= v
->Next()) {
2353 result
+= v
->cargo_cap
;
2359 * Send this vehicle to the depot using the given command(s).
2360 * @param flags the command flags (like execute and such).
2361 * @param command the command to execute.
2362 * @return the cost of the depot action.
2364 CommandCost
Vehicle::SendToDepot(DoCommandFlag flags
, DepotCommand command
)
2366 CommandCost ret
= CheckOwnership(this->owner
);
2367 if (ret
.Failed()) return ret
;
2369 if (this->vehstatus
& VS_CRASHED
) return CMD_ERROR
;
2370 if (this->IsStoppedInDepot()) return CMD_ERROR
;
2372 if (this->current_order
.IsType(OT_GOTO_DEPOT
)) {
2373 bool halt_in_depot
= (this->current_order
.GetDepotActionType() & ODATFB_HALT
) != 0;
2374 if (!!(command
& DEPOT_SERVICE
) == halt_in_depot
) {
2375 /* We called with a different DEPOT_SERVICE setting.
2376 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2377 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2378 if (flags
& DC_EXEC
) {
2379 this->current_order
.SetDepotOrderType(ODTF_MANUAL
);
2380 this->current_order
.SetDepotActionType(halt_in_depot
? ODATF_SERVICE_ONLY
: ODATFB_HALT
);
2381 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2383 return CommandCost();
2386 if (command
& DEPOT_DONT_CANCEL
) return CMD_ERROR
; // Requested no cancellation of depot orders
2387 if (flags
& DC_EXEC
) {
2388 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2389 * then skip to the next order; effectively cancelling this forced service */
2390 if (this->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) this->IncrementRealOrderIndex();
2392 if (this->IsGroundVehicle()) {
2393 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2394 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2397 this->current_order
.MakeDummy();
2398 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2400 return CommandCost();
2404 DestinationID destination
;
2406 static const StringID no_depot
[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO
, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT
, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT
, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR
};
2407 if (!this->FindClosestDepot(&location
, &destination
, &reverse
)) return_cmd_error(no_depot
[this->type
]);
2409 if (flags
& DC_EXEC
) {
2410 if (this->current_order
.IsType(OT_LOADING
)) this->LeaveStation();
2412 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2413 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2414 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2417 this->SetDestTile(location
);
2418 this->current_order
.MakeGoToDepot(destination
, ODTF_MANUAL
);
2419 if (!(command
& DEPOT_SERVICE
)) this->current_order
.SetDepotActionType(ODATFB_HALT
);
2420 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2422 /* If there is no depot in front and the train is not already reversing, reverse automatically (trains only) */
2423 if (this->type
== VEH_TRAIN
&& (reverse
^ HasBit(Train::From(this)->flags
, VRF_REVERSING
))) {
2424 DoCommand(this->tile
, this->index
, 0, DC_EXEC
, CMD_REVERSE_TRAIN_DIRECTION
);
2427 if (this->type
== VEH_AIRCRAFT
) {
2428 Aircraft
*a
= Aircraft::From(this);
2429 if (a
->state
== FLYING
&& a
->targetairport
!= destination
) {
2430 /* The aircraft is now heading for a different hangar than the next in the orders */
2431 extern void AircraftNextAirportPos_and_Order(Aircraft
*a
);
2432 AircraftNextAirportPos_and_Order(a
);
2437 return CommandCost();
2442 * Update the cached visual effect.
2443 * @param allow_power_change true if the wagon-is-powered-state may change.
2445 void Vehicle::UpdateVisualEffect(bool allow_power_change
)
2447 bool powered_before
= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2448 const Engine
*e
= this->GetEngine();
2450 /* Evaluate properties */
2453 case VEH_TRAIN
: visual_effect
= e
->u
.rail
.visual_effect
; break;
2454 case VEH_ROAD
: visual_effect
= e
->u
.road
.visual_effect
; break;
2455 case VEH_SHIP
: visual_effect
= e
->u
.ship
.visual_effect
; break;
2456 default: visual_effect
= 1 << VE_DISABLE_EFFECT
; break;
2459 /* Check powered wagon / visual effect callback */
2460 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_VISUAL_EFFECT
)) {
2461 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT
, 0, 0, this->engine_type
, this);
2463 if (callback
!= CALLBACK_FAILED
) {
2464 if (callback
>= 0x100 && e
->GetGRF()->grf_version
>= 8) ErrorUnknownCallbackResult(e
->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT
, callback
);
2466 callback
= GB(callback
, 0, 8);
2467 /* Avoid accidentally setting 'visual_effect' to the default value
2468 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2469 if (callback
== VE_DEFAULT
) {
2470 assert(HasBit(callback
, VE_DISABLE_EFFECT
));
2471 SB(callback
, VE_TYPE_START
, VE_TYPE_COUNT
, 0);
2473 visual_effect
= callback
;
2477 /* Apply default values */
2478 if (visual_effect
== VE_DEFAULT
||
2479 (!HasBit(visual_effect
, VE_DISABLE_EFFECT
) && GB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
) == VE_TYPE_DEFAULT
)) {
2480 /* Only train engines have default effects.
2481 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2482 if (e
->type
!= VEH_TRAIN
|| e
->u
.rail
.railveh_type
== RAILVEH_WAGON
|| !IsInsideMM(e
->u
.rail
.engclass
, EC_STEAM
, EC_MONORAIL
)) {
2483 if (visual_effect
== VE_DEFAULT
) {
2484 visual_effect
= 1 << VE_DISABLE_EFFECT
;
2486 SetBit(visual_effect
, VE_DISABLE_EFFECT
);
2489 if (visual_effect
== VE_DEFAULT
) {
2490 /* Also set the offset */
2491 visual_effect
= (VE_OFFSET_CENTRE
- (e
->u
.rail
.engclass
== EC_STEAM
? 4 : 0)) << VE_OFFSET_START
;
2493 SB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
, e
->u
.rail
.engclass
- EC_STEAM
+ VE_TYPE_STEAM
);
2497 this->vcache
.cached_vis_effect
= visual_effect
;
2499 if (!allow_power_change
&& powered_before
!= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
)) {
2500 ToggleBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2501 ShowNewGrfVehicleError(this->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_POWERED_WAGON
, GBUG_VEH_POWERED_WAGON
, false);
2505 static const int8 _vehicle_smoke_pos
[8] = {
2506 1, 1, 1, 0, -1, -1, -1, 0
2510 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2511 * @param v Vehicle to create effects for.
2513 static void SpawnAdvancedVisualEffect(const Vehicle
*v
)
2515 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT
, 0, Random(), v
->engine_type
, v
);
2516 if (callback
== CALLBACK_FAILED
) return;
2518 uint count
= GB(callback
, 0, 2);
2519 bool auto_center
= HasBit(callback
, 13);
2520 bool auto_rotate
= !HasBit(callback
, 14);
2524 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2525 if (v
->type
== VEH_ROAD
) l_center
= -(int)(VEHICLE_LENGTH
- RoadVehicle::From(v
)->gcache
.cached_veh_length
) / 2;
2527 /* For trains: Compute offset from vehicle position to sprite position */
2528 if (v
->type
== VEH_TRAIN
) l_center
= (VEHICLE_LENGTH
- Train::From(v
)->gcache
.cached_veh_length
) / 2;
2531 Direction l_dir
= v
->direction
;
2532 if (v
->type
== VEH_TRAIN
&& HasBit(Train::From(v
)->flags
, VRF_REVERSE_DIRECTION
)) l_dir
= ReverseDir(l_dir
);
2533 Direction t_dir
= ChangeDir(l_dir
, DIRDIFF_90RIGHT
);
2535 int8 x_center
= _vehicle_smoke_pos
[l_dir
] * l_center
;
2536 int8 y_center
= _vehicle_smoke_pos
[t_dir
] * l_center
;
2538 for (uint i
= 0; i
< count
; i
++) {
2539 uint32 reg
= GetRegister(0x100 + i
);
2540 uint type
= GB(reg
, 0, 8);
2541 int8 x
= GB(reg
, 8, 8);
2542 int8 y
= GB(reg
, 16, 8);
2543 int8 z
= GB(reg
, 24, 8);
2548 x
= _vehicle_smoke_pos
[l_dir
] * l
+ _vehicle_smoke_pos
[t_dir
] * t
;
2549 y
= _vehicle_smoke_pos
[t_dir
] * l
- _vehicle_smoke_pos
[l_dir
] * t
;
2554 case 0xF1: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_STEAM_SMOKE
); break;
2555 case 0xF2: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_DIESEL_SMOKE
); break;
2556 case 0xF3: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_ELECTRIC_SPARK
); break;
2557 case 0xFA: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_BREAKDOWN_SMOKE_AIRCRAFT
); break;
2565 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2566 * @pre this->IsPrimaryVehicle()
2568 void Vehicle::ShowVisualEffect() const
2570 assert(this->IsPrimaryVehicle());
2573 /* Do not show any smoke when:
2574 * - vehicle smoke is disabled by the player
2575 * - the vehicle is slowing down or stopped (by the player)
2576 * - the vehicle is moving very slowly
2578 if (_settings_game
.vehicle
.smoke_amount
== 0 ||
2579 this->vehstatus
& (VS_TRAIN_SLOWING
| VS_STOPPED
) ||
2580 this->cur_speed
< 2) {
2584 /* Use the speed as limited by underground and orders. */
2585 uint max_speed
= this->GetCurrentMaxSpeed();
2587 if (this->type
== VEH_TRAIN
) {
2588 const Train
*t
= Train::From(this);
2589 /* For trains, do not show any smoke when:
2590 * - the train is reversing
2591 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2593 if (HasBit(t
->flags
, VRF_REVERSING
) ||
2594 (IsRailStationTile(t
->tile
) && t
->IsFrontEngine() && t
->current_order
.ShouldStopAtStation(t
, GetStationIndex(t
->tile
)) &&
2595 t
->cur_speed
>= max_speed
)) {
2600 const Vehicle
*v
= this;
2603 bool advanced
= HasBit(v
->vcache
.cached_vis_effect
, VE_ADVANCED_EFFECT
);
2604 int effect_offset
= GB(v
->vcache
.cached_vis_effect
, VE_OFFSET_START
, VE_OFFSET_COUNT
) - VE_OFFSET_CENTRE
;
2605 VisualEffectSpawnModel effect_model
= VESM_NONE
;
2607 effect_offset
= VE_OFFSET_CENTRE
;
2608 effect_model
= (VisualEffectSpawnModel
)GB(v
->vcache
.cached_vis_effect
, 0, VE_ADVANCED_EFFECT
);
2609 if (effect_model
>= VESM_END
) effect_model
= VESM_NONE
; // unknown spawning model
2611 effect_model
= (VisualEffectSpawnModel
)GB(v
->vcache
.cached_vis_effect
, VE_TYPE_START
, VE_TYPE_COUNT
);
2612 assert(effect_model
!= (VisualEffectSpawnModel
)VE_TYPE_DEFAULT
); // should have been resolved by UpdateVisualEffect
2613 static_assert((uint
)VESM_STEAM
== (uint
)VE_TYPE_STEAM
);
2614 static_assert((uint
)VESM_DIESEL
== (uint
)VE_TYPE_DIESEL
);
2615 static_assert((uint
)VESM_ELECTRIC
== (uint
)VE_TYPE_ELECTRIC
);
2618 /* Show no smoke when:
2619 * - Smoke has been disabled for this vehicle
2620 * - The vehicle is not visible
2621 * - The vehicle is under a bridge
2622 * - The vehicle is on a depot tile
2623 * - The vehicle is on a tunnel tile
2624 * - The vehicle is a train engine that is currently unpowered */
2625 if (effect_model
== VESM_NONE
||
2626 v
->vehstatus
& VS_HIDDEN
||
2627 IsBridgeAbove(v
->tile
) ||
2628 IsDepotTile(v
->tile
) ||
2629 IsTunnelTile(v
->tile
) ||
2630 (v
->type
== VEH_TRAIN
&&
2631 !HasPowerOnRail(Train::From(v
)->railtype
, GetTileRailType(v
->tile
)))) {
2635 EffectVehicleType evt
= EV_END
;
2636 switch (effect_model
) {
2638 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2639 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2640 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2642 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2643 if (GB(v
->tick_counter
, 0, ((4 >> _settings_game
.vehicle
.smoke_amount
) + ((this->cur_speed
* 3) / max_speed
))) == 0) {
2644 evt
= EV_STEAM_SMOKE
;
2649 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2650 * when smoke emission stops.
2651 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2652 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2653 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2654 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2655 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2656 * maximum speed no diesel_smoke is emitted.
2658 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2659 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2660 int power_weight_effect
= 0;
2661 if (v
->type
== VEH_TRAIN
) {
2662 power_weight_effect
= (32 >> (Train::From(this)->gcache
.cached_power
>> 10)) - (32 >> (Train::From(this)->gcache
.cached_weight
>> 9));
2664 if (this->cur_speed
< (max_speed
>> (2 >> _settings_game
.vehicle
.smoke_amount
)) &&
2665 Chance16((64 - ((this->cur_speed
<< 5) / max_speed
) + power_weight_effect
), (512 >> _settings_game
.vehicle
.smoke_amount
))) {
2666 evt
= EV_DIESEL_SMOKE
;
2672 /* Electric train's spark - more often occurs when train is departing (more load)
2673 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2674 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2675 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2677 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2678 if (GB(v
->tick_counter
, 0, 2) == 0 &&
2679 Chance16((6 - ((this->cur_speed
<< 2) / max_speed
)), (360 >> _settings_game
.vehicle
.smoke_amount
))) {
2680 evt
= EV_ELECTRIC_SPARK
;
2688 if (evt
!= EV_END
&& advanced
) {
2690 SpawnAdvancedVisualEffect(v
);
2691 } else if (evt
!= EV_END
) {
2694 /* The effect offset is relative to a point 4 units behind the vehicle's
2695 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2696 * correction factor. */
2697 if (v
->type
== VEH_TRAIN
) effect_offset
+= (VEHICLE_LENGTH
- Train::From(v
)->gcache
.cached_veh_length
) / 2;
2699 int x
= _vehicle_smoke_pos
[v
->direction
] * effect_offset
;
2700 int y
= _vehicle_smoke_pos
[(v
->direction
+ 2) % 8] * effect_offset
;
2702 if (v
->type
== VEH_TRAIN
&& HasBit(Train::From(v
)->flags
, VRF_REVERSE_DIRECTION
)) {
2707 CreateEffectVehicleRel(v
, x
, y
, 10, evt
);
2709 } while ((v
= v
->Next()) != nullptr);
2711 if (sound
) PlayVehicleSound(this, VSE_VISUAL_EFFECT
);
2715 * Set the next vehicle of this vehicle.
2716 * @param next the next vehicle. nullptr removes the next vehicle.
2718 void Vehicle::SetNext(Vehicle
*next
)
2720 assert(this != next
);
2722 if (this->next
!= nullptr) {
2723 /* We had an old next vehicle. Update the first and previous pointers */
2724 for (Vehicle
*v
= this->next
; v
!= nullptr; v
= v
->Next()) {
2725 v
->first
= this->next
;
2727 this->next
->previous
= nullptr;
2732 if (this->next
!= nullptr) {
2733 /* A new next vehicle. Update the first and previous pointers */
2734 if (this->next
->previous
!= nullptr) this->next
->previous
->next
= nullptr;
2735 this->next
->previous
= this;
2736 for (Vehicle
*v
= this->next
; v
!= nullptr; v
= v
->Next()) {
2737 v
->first
= this->first
;
2743 * Adds this vehicle to a shared vehicle chain.
2744 * @param shared_chain a vehicle of the chain with shared vehicles.
2745 * @pre !this->IsOrderListShared()
2747 void Vehicle::AddToShared(Vehicle
*shared_chain
)
2749 assert(this->previous_shared
== nullptr && this->next_shared
== nullptr);
2751 if (shared_chain
->orders
.list
== nullptr) {
2752 assert(shared_chain
->previous_shared
== nullptr);
2753 assert(shared_chain
->next_shared
== nullptr);
2754 this->orders
.list
= shared_chain
->orders
.list
= new OrderList(nullptr, shared_chain
);
2757 this->next_shared
= shared_chain
->next_shared
;
2758 this->previous_shared
= shared_chain
;
2760 shared_chain
->next_shared
= this;
2762 if (this->next_shared
!= nullptr) this->next_shared
->previous_shared
= this;
2764 shared_chain
->orders
.list
->AddVehicle(this);
2768 * Removes the vehicle from the shared order list.
2770 void Vehicle::RemoveFromShared()
2772 /* Remember if we were first and the old window number before RemoveVehicle()
2773 * as this changes first if needed. */
2774 bool were_first
= (this->FirstShared() == this);
2775 VehicleListIdentifier
vli(VL_SHARED_ORDERS
, this->type
, this->owner
, this->FirstShared()->index
);
2777 this->orders
.list
->RemoveVehicle(this);
2780 /* We are not the first shared one, so only relink our previous one. */
2781 this->previous_shared
->next_shared
= this->NextShared();
2784 if (this->next_shared
!= nullptr) this->next_shared
->previous_shared
= this->previous_shared
;
2787 if (this->orders
.list
->GetNumVehicles() == 1) {
2788 /* When there is only one vehicle, remove the shared order list window. */
2789 DeleteWindowById(GetWindowClassForVehicleType(this->type
), vli
.Pack());
2790 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS
);
2791 } else if (were_first
) {
2792 /* If we were the first one, update to the new first one.
2793 * Note: FirstShared() is already the new first */
2794 InvalidateWindowData(GetWindowClassForVehicleType(this->type
), vli
.Pack(), this->FirstShared()->index
| (1U << 31));
2797 this->next_shared
= nullptr;
2798 this->previous_shared
= nullptr;
2801 void VehiclesYearlyLoop()
2803 for (Vehicle
*v
: Vehicle::Iterate()) {
2804 if (v
->IsPrimaryVehicle()) {
2805 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2806 Money profit
= v
->GetDisplayProfitThisYear();
2807 if (v
->age
>= 730 && profit
< 0) {
2808 if (_settings_client
.gui
.vehicle_income_warn
&& v
->owner
== _local_company
) {
2809 SetDParam(0, v
->index
);
2810 SetDParam(1, profit
);
2811 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE
, v
->index
);
2813 AI::NewEvent(v
->owner
, new ScriptEventVehicleUnprofitable(v
->index
));
2816 v
->profit_last_year
= v
->profit_this_year
;
2817 v
->profit_this_year
= 0;
2818 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
2821 GroupStatistics::UpdateProfits();
2822 SetWindowClassesDirty(WC_TRAINS_LIST
);
2823 SetWindowClassesDirty(WC_SHIPS_LIST
);
2824 SetWindowClassesDirty(WC_ROADVEH_LIST
);
2825 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
2830 * Can this station be used by the given engine type?
2831 * @param engine_type the type of vehicles to test
2832 * @param st the station to test for
2833 * @return true if and only if the vehicle of the type can use this station.
2834 * @note For road vehicles the Vehicle is needed to determine whether it can
2835 * use the station. This function will return true for road vehicles
2836 * when at least one of the facilities is available.
2838 bool CanVehicleUseStation(EngineID engine_type
, const Station
*st
)
2840 const Engine
*e
= Engine::GetIfValid(engine_type
);
2841 assert(e
!= nullptr);
2845 return (st
->facilities
& FACIL_TRAIN
) != 0;
2848 /* For road vehicles we need the vehicle to know whether it can actually
2849 * use the station, but if it doesn't have facilities for RVs it is
2850 * certainly not possible that the station can be used. */
2851 return (st
->facilities
& (FACIL_BUS_STOP
| FACIL_TRUCK_STOP
)) != 0;
2854 return (st
->facilities
& FACIL_DOCK
) != 0;
2857 return (st
->facilities
& FACIL_AIRPORT
) != 0 &&
2858 (st
->airport
.GetFTA()->flags
& (e
->u
.air
.subtype
& AIR_CTOL
? AirportFTAClass::AIRPLANES
: AirportFTAClass::HELICOPTERS
)) != 0;
2866 * Can this station be used by the given vehicle?
2867 * @param v the vehicle to test
2868 * @param st the station to test for
2869 * @return true if and only if the vehicle can use this station.
2871 bool CanVehicleUseStation(const Vehicle
*v
, const Station
*st
)
2873 if (v
->type
== VEH_ROAD
) return st
->GetPrimaryRoadStop(RoadVehicle::From(v
)) != nullptr;
2875 return CanVehicleUseStation(v
->engine_type
, st
);
2879 * Access the ground vehicle cache of the vehicle.
2880 * @pre The vehicle is a #GroundVehicle.
2881 * @return #GroundVehicleCache of the vehicle.
2883 GroundVehicleCache
*Vehicle::GetGroundVehicleCache()
2885 assert(this->IsGroundVehicle());
2886 if (this->type
== VEH_TRAIN
) {
2887 return &Train::From(this)->gcache
;
2889 return &RoadVehicle::From(this)->gcache
;
2894 * Access the ground vehicle cache of the vehicle.
2895 * @pre The vehicle is a #GroundVehicle.
2896 * @return #GroundVehicleCache of the vehicle.
2898 const GroundVehicleCache
*Vehicle::GetGroundVehicleCache() const
2900 assert(this->IsGroundVehicle());
2901 if (this->type
== VEH_TRAIN
) {
2902 return &Train::From(this)->gcache
;
2904 return &RoadVehicle::From(this)->gcache
;
2909 * Access the ground vehicle flags of the vehicle.
2910 * @pre The vehicle is a #GroundVehicle.
2911 * @return #GroundVehicleFlags of the vehicle.
2913 uint16
&Vehicle::GetGroundVehicleFlags()
2915 assert(this->IsGroundVehicle());
2916 if (this->type
== VEH_TRAIN
) {
2917 return Train::From(this)->gv_flags
;
2919 return RoadVehicle::From(this)->gv_flags
;
2924 * Access the ground vehicle flags of the vehicle.
2925 * @pre The vehicle is a #GroundVehicle.
2926 * @return #GroundVehicleFlags of the vehicle.
2928 const uint16
&Vehicle::GetGroundVehicleFlags() const
2930 assert(this->IsGroundVehicle());
2931 if (this->type
== VEH_TRAIN
) {
2932 return Train::From(this)->gv_flags
;
2934 return RoadVehicle::From(this)->gv_flags
;
2939 * Calculates the set of vehicles that will be affected by a given selection.
2940 * @param[in,out] set Set of affected vehicles.
2941 * @param v First vehicle of the selection.
2942 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2943 * @pre \a set must be empty.
2944 * @post \a set will contain the vehicles that will be refitted.
2946 void GetVehicleSet(VehicleSet
&set
, Vehicle
*v
, uint8 num_vehicles
)
2948 if (v
->type
== VEH_TRAIN
) {
2949 Train
*u
= Train::From(v
);
2950 /* Only include whole vehicles, so start with the first articulated part */
2951 u
= u
->GetFirstEnginePart();
2953 /* Include num_vehicles vehicles, not counting articulated parts */
2954 for (; u
!= nullptr && num_vehicles
> 0; num_vehicles
--) {
2956 /* Include current vehicle in the selection. */
2957 include(set
, u
->index
);
2959 /* If the vehicle is multiheaded, add the other part too. */
2960 if (u
->IsMultiheaded()) include(set
, u
->other_multiheaded_part
->index
);
2963 } while (u
!= nullptr && u
->IsArticulatedPart());