Codechange: Use null pointer literal instead of the NULL macro
[openttd-github.git] / src / vehicle.cpp
blobe6df365b052aed08e10bb70a3b8273a24c09f114
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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 */
10 /** @file vehicle.cpp Base implementations of all vehicles. */
12 #include "stdafx.h"
13 #include "error.h"
14 #include "roadveh.h"
15 #include "ship.h"
16 #include "spritecache.h"
17 #include "timetable.h"
18 #include "viewport_func.h"
19 #include "news_func.h"
20 #include "command_func.h"
21 #include "company_func.h"
22 #include "train.h"
23 #include "aircraft.h"
24 #include "newgrf_debug.h"
25 #include "newgrf_sound.h"
26 #include "newgrf_station.h"
27 #include "group_gui.h"
28 #include "strings_func.h"
29 #include "zoom_func.h"
30 #include "date_func.h"
31 #include "vehicle_func.h"
32 #include "autoreplace_func.h"
33 #include "autoreplace_gui.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "depot_func.h"
37 #include "network/network.h"
38 #include "core/pool_func.hpp"
39 #include "economy_base.h"
40 #include "articulated_vehicles.h"
41 #include "roadstop_base.h"
42 #include "core/random_func.hpp"
43 #include "core/backup_type.hpp"
44 #include "order_backup.h"
45 #include "sound_func.h"
46 #include "effectvehicle_func.h"
47 #include "effectvehicle_base.h"
48 #include "vehiclelist.h"
49 #include "bridge_map.h"
50 #include "tunnel_map.h"
51 #include "depot_map.h"
52 #include "gamelog.h"
53 #include "linkgraph/linkgraph.h"
54 #include "linkgraph/refresh.h"
55 #include "framerate_type.h"
57 #include "table/strings.h"
59 #include "safeguards.h"
61 /* Number of bits in the hash to use from each vehicle coord */
62 static const uint GEN_HASHX_BITS = 6;
63 static const uint GEN_HASHY_BITS = 6;
65 /* Size of each hash bucket */
66 static const uint GEN_HASHX_BUCKET_BITS = 7;
67 static const uint GEN_HASHY_BUCKET_BITS = 6;
69 /* Compute hash for vehicle coord */
70 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
71 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
72 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
74 /* Maximum size until hash repeats */
75 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_LVL_SHIFT);
76 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_LVL_SHIFT);
78 /* Increments to reach next bucket in hash table */
79 static const int GEN_HASHX_INC = 1;
80 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
82 /* Mask to wrap-around buckets */
83 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
84 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
86 VehicleID _new_vehicle_id;
87 uint16 _returned_refit_capacity; ///< Stores the capacity after a refit operation.
88 uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refit operation (Aircraft only).
91 /** The pool with all our precious vehicles. */
92 VehiclePool _vehicle_pool("Vehicle");
93 INSTANTIATE_POOL_METHODS(Vehicle)
96 /**
97 * Determine shared bounds of all sprites.
98 * @param[out] bounds Shared bounds.
100 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
102 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
103 for (uint i = 0; i < this->count; ++i) {
104 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
105 if (i == 0) {
106 bounds->left = spr->x_offs;
107 bounds->top = spr->y_offs;
108 bounds->right = spr->width + spr->x_offs - 1;
109 bounds->bottom = spr->height + spr->y_offs - 1;
110 } else {
111 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
112 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
113 int right = spr->width + spr->x_offs - 1;
114 int bottom = spr->height + spr->y_offs - 1;
115 if (right > bounds->right) bounds->right = right;
116 if (bottom > bounds->bottom) bounds->bottom = bottom;
122 * Draw the sprite sequence.
123 * @param x X position
124 * @param y Y position
125 * @param default_pal Vehicle palette
126 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
128 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
130 for (uint i = 0; i < this->count; ++i) {
131 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
132 DrawSprite(this->seq[i].sprite, pal, x, y);
137 * Function to tell if a vehicle needs to be autorenewed
138 * @param *c The vehicle owner
139 * @param use_renew_setting Should the company renew setting be considered?
140 * @return true if the vehicle is old enough for replacement
142 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
144 /* We can always generate the Company pointer when we have the vehicle.
145 * However this takes time and since the Company pointer is often present
146 * when this function is called then it's faster to pass the pointer as an
147 * argument rather than finding it again. */
148 assert(c == Company::Get(this->owner));
150 if (use_renew_setting && !c->settings.engine_renew) return false;
151 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
153 /* Only engines need renewing */
154 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
156 return true;
160 * Service a vehicle and all subsequent vehicles in the consist
162 * @param *v The vehicle or vehicle chain being serviced
164 void VehicleServiceInDepot(Vehicle *v)
166 assert(v != nullptr);
167 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
169 do {
170 v->date_of_last_service = _date;
171 v->breakdowns_since_last_service = 0;
172 v->reliability = v->GetEngine()->reliability;
173 /* Prevent vehicles from breaking down directly after exiting the depot. */
174 v->breakdown_chance /= 4;
175 v = v->Next();
176 } while (v != nullptr && v->HasEngineType());
180 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
182 * @see NeedsAutomaticServicing()
183 * @return true if the vehicle should go to a depot if a opportunity presents itself.
185 bool Vehicle::NeedsServicing() const
187 /* Stopped or crashed vehicles will not move, as such making unmovable
188 * vehicles to go for service is lame. */
189 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
191 /* Are we ready for the next service cycle? */
192 const Company *c = Company::Get(this->owner);
193 if (this->ServiceIntervalIsPercent() ?
194 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
195 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
196 return false;
199 /* If we're servicing anyway, because we have not disabled servicing when
200 * there are no breakdowns or we are playing with breakdowns, bail out. */
201 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
202 _settings_game.difficulty.vehicle_breakdowns != 0) {
203 return true;
206 /* Test whether there is some pending autoreplace.
207 * Note: We do this after the service-interval test.
208 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
209 bool pending_replace = false;
210 Money needed_money = c->settings.engine_renew_money;
211 if (needed_money > c->money) return false;
213 for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) {
214 bool replace_when_old = false;
215 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
217 /* Check engine availability */
218 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
219 /* Is the vehicle old if we are not always replacing? */
220 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
222 /* Check refittability */
223 CargoTypes available_cargo_types, union_mask;
224 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
225 /* Is there anything to refit? */
226 if (union_mask != 0) {
227 CargoID cargo_type;
228 /* We cannot refit to mixed cargoes in an automated way */
229 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
231 /* Did the old vehicle carry anything? */
232 if (cargo_type != CT_INVALID) {
233 /* We can't refit the vehicle to carry the cargo we want */
234 if (!HasBit(available_cargo_types, cargo_type)) continue;
238 /* Check money.
239 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
240 pending_replace = true;
241 needed_money += 2 * Engine::Get(new_engine)->GetCost();
242 if (needed_money > c->money) return false;
245 return pending_replace;
249 * Checks if the current order should be interrupted for a service-in-depot order.
250 * @see NeedsServicing()
251 * @return true if the current order should be interrupted.
253 bool Vehicle::NeedsAutomaticServicing() const
255 if (this->HasDepotOrder()) return false;
256 if (this->current_order.IsType(OT_LOADING)) return false;
257 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
258 return NeedsServicing();
261 uint Vehicle::Crash(bool flooded)
263 assert((this->vehstatus & VS_CRASHED) == 0);
264 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
266 uint pass = 0;
267 /* Stop the vehicle. */
268 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
269 /* crash all wagons, and count passengers */
270 for (Vehicle *v = this; v != nullptr; v = v->Next()) {
271 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
272 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
273 v->vehstatus |= VS_CRASHED;
274 v->MarkAllViewportsDirty();
277 /* Dirty some windows */
278 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
279 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
280 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
281 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
283 delete this->cargo_payment;
284 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
286 return RandomRange(pass + 1); // Randomise deceased passengers.
291 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
292 * @param engine The engine that caused the problem
293 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
294 * @param part2 Part 2 of the error message, taking the engine as parameter 2
295 * @param bug_type Flag to check and set in grfconfig
296 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
298 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
300 const Engine *e = Engine::Get(engine);
301 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
303 /* Missing GRF. Nothing useful can be done in this situation. */
304 if (grfconfig == nullptr) return;
306 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
307 SetBit(grfconfig->grf_bugs, bug_type);
308 SetDParamStr(0, grfconfig->GetName());
309 SetDParam(1, engine);
310 ShowErrorMessage(part1, part2, WL_CRITICAL);
311 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
314 /* debug output */
315 char buffer[512];
317 SetDParamStr(0, grfconfig->GetName());
318 GetString(buffer, part1, lastof(buffer));
319 DEBUG(grf, 0, "%s", buffer + 3);
321 SetDParam(1, engine);
322 GetString(buffer, part2, lastof(buffer));
323 DEBUG(grf, 0, "%s", buffer + 3);
327 * Logs a bug in GRF and shows a warning message if this
328 * is for the first time this happened.
329 * @param u first vehicle of chain
331 void VehicleLengthChanged(const Vehicle *u)
333 /* show a warning once for each engine in whole game and once for each GRF after each game load */
334 const Engine *engine = u->GetEngine();
335 uint32 grfid = engine->grf_prop.grffile->grfid;
336 GRFConfig *grfconfig = GetGRFConfig(grfid);
337 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
338 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
343 * Vehicle constructor.
344 * @param type Type of the new vehicle.
346 Vehicle::Vehicle(VehicleType type)
348 this->type = type;
349 this->coord.left = INVALID_COORD;
350 this->group_id = DEFAULT_GROUP;
351 this->fill_percent_te_id = INVALID_TE_ID;
352 this->first = this;
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;
391 if (x == xu) break;
393 if (y == yu) break;
396 return nullptr;
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
408 * all vehicles
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
466 * all vehicles
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;
482 return nullptr;
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)
526 int z = *(int*)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;
531 return v;
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;
558 return v;
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;
590 return v;
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;
615 Vehicle **new_hash;
617 if (remove) {
618 new_hash = nullptr;
619 } else {
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;
638 *new_hash = v;
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)
649 Vehicle **old_hash, **new_hash;
650 int old_x = v->coord.left;
651 int old_y = v->coord.top;
653 new_hash = (x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(x, y)];
654 old_hash = (old_x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
656 if (old_hash == new_hash) return;
658 /* remove from hash table? */
659 if (old_hash != nullptr) {
660 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
661 *v->hash_viewport_prev = v->hash_viewport_next;
664 /* insert into hash table? */
665 if (new_hash != nullptr) {
666 v->hash_viewport_next = *new_hash;
667 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
668 v->hash_viewport_prev = new_hash;
669 *new_hash = v;
673 void ResetVehicleHash()
675 Vehicle *v;
676 FOR_ALL_VEHICLES(v) { v->hash_tile_current = nullptr; }
677 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
678 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
681 void ResetVehicleColourMap()
683 Vehicle *v;
684 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
688 * List of vehicles that should check for autoreplace this tick.
689 * Mapping of vehicle -> leave depot immediately after autoreplace.
691 typedef SmallMap<Vehicle *, bool> AutoreplaceMap;
692 static AutoreplaceMap _vehicles_to_autoreplace;
694 void InitializeVehicles()
696 _vehicles_to_autoreplace.clear();
697 _vehicles_to_autoreplace.shrink_to_fit();
698 ResetVehicleHash();
701 uint CountVehiclesInChain(const Vehicle *v)
703 uint count = 0;
704 do count++; while ((v = v->Next()) != nullptr);
705 return count;
709 * Check if a vehicle is counted in num_engines in each company struct
710 * @return true if the vehicle is counted in num_engines
712 bool Vehicle::IsEngineCountable() const
714 switch (this->type) {
715 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
716 case VEH_TRAIN:
717 return !this->IsArticulatedPart() && // tenders and other articulated parts
718 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
719 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
720 case VEH_SHIP: return true;
721 default: return false; // Only count company buildable vehicles
726 * Check whether Vehicle::engine_type has any meaning.
727 * @return true if the vehicle has a usable engine type.
729 bool Vehicle::HasEngineType() const
731 switch (this->type) {
732 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
733 case VEH_TRAIN:
734 case VEH_ROAD:
735 case VEH_SHIP: return true;
736 default: return false;
741 * Retrieves the engine of the vehicle.
742 * @return Engine of the vehicle.
743 * @pre HasEngineType() == true
745 const Engine *Vehicle::GetEngine() const
747 return Engine::Get(this->engine_type);
751 * Retrieve the NewGRF the vehicle is tied to.
752 * This is the GRF providing the Action 3 for the engine type.
753 * @return NewGRF associated to the vehicle.
755 const GRFFile *Vehicle::GetGRF() const
757 return this->GetEngine()->GetGRF();
761 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
762 * This is the GRF providing the Action 3 for the engine type.
763 * @return GRF ID of the associated NewGRF.
765 uint32 Vehicle::GetGRFID() const
767 return this->GetEngine()->GetGRFID();
771 * Handle the pathfinding result, especially the lost status.
772 * If the vehicle is now lost and wasn't previously fire an
773 * event to the AIs and a news message to the user. If the
774 * vehicle is not lost anymore remove the news message.
775 * @param path_found Whether the vehicle has a path to its destination.
777 void Vehicle::HandlePathfindingResult(bool path_found)
779 if (path_found) {
780 /* Route found, is the vehicle marked with "lost" flag? */
781 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
783 /* Clear the flag as the PF's problem was solved. */
784 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
785 /* Delete the news item. */
786 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
787 return;
790 /* Were we already lost? */
791 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
793 /* It is first time the problem occurred, set the "lost" flag. */
794 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
795 /* Notify user about the event. */
796 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
797 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
798 SetDParam(0, this->index);
799 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
803 /** Destroy all stuff that (still) needs the virtual functions to work properly */
804 void Vehicle::PreDestructor()
806 if (CleaningPool()) return;
808 if (Station::IsValidID(this->last_station_visited)) {
809 Station *st = Station::Get(this->last_station_visited);
810 st->loading_vehicles.remove(this);
812 HideFillingPercent(&this->fill_percent_te_id);
813 this->CancelReservation(INVALID_STATION, st);
814 delete this->cargo_payment;
815 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
818 if (this->IsEngineCountable()) {
819 GroupStatistics::CountEngine(this, -1);
820 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
821 GroupStatistics::UpdateAutoreplace(this->owner);
823 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
824 DeleteGroupHighlightOfVehicle(this);
827 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
828 Aircraft *a = Aircraft::From(this);
829 Station *st = GetTargetAirportIfValid(a);
830 if (st != nullptr) {
831 const AirportFTA *layout = st->airport.GetFTA()->layout;
832 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
837 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
838 RoadVehicle *v = RoadVehicle::From(this);
839 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
840 /* Leave the drive through roadstop, when you have not already left it. */
841 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
845 if (this->Previous() == nullptr) {
846 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
849 if (this->IsPrimaryVehicle()) {
850 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
851 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
852 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
853 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
854 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
855 SetWindowDirty(WC_COMPANY, this->owner);
856 OrderBackup::ClearVehicle(this);
858 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
860 this->cargo.Truncate();
861 DeleteVehicleOrders(this);
862 DeleteDepotHighlightOfVehicle(this);
864 extern void StopGlobalFollowVehicle(const Vehicle *v);
865 StopGlobalFollowVehicle(this);
867 ReleaseDisastersTargetingVehicle(this->index);
870 Vehicle::~Vehicle()
872 if (CleaningPool()) {
873 this->cargo.OnCleanPool();
874 return;
877 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
878 * it may happen that vehicle chain is deleted when visible */
879 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
881 Vehicle *v = this->Next();
882 this->SetNext(nullptr);
884 delete v;
886 UpdateVehicleTileHash(this, true);
887 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
888 DeleteVehicleNews(this->index, INVALID_STRING_ID);
889 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
893 * Adds a vehicle to the list of vehicles that visited a depot this tick
894 * @param *v vehicle to add
896 void VehicleEnteredDepotThisTick(Vehicle *v)
898 /* Vehicle should stop in the depot if it was in 'stopping' state */
899 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
901 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
902 * stopping in the depot, so we stop it to ensure that it will not reserve
903 * the path out of the depot before we might autoreplace it to a different
904 * engine. The new engine would not own the reserved path we store that we
905 * stopped the vehicle, so autoreplace can start it again */
906 v->vehstatus |= VS_STOPPED;
910 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
911 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
912 * so each day, all vehicles are processes in DAY_TICKS steps.
914 static void RunVehicleDayProc()
916 if (_game_mode != GM_NORMAL) return;
918 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
919 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
920 Vehicle *v = Vehicle::Get(i);
921 if (v == nullptr) continue;
923 /* Call the 32-day callback if needed */
924 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
925 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
926 if (callback != CALLBACK_FAILED) {
927 if (HasBit(callback, 0)) {
928 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
931 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
932 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
933 if (callback != 0) v->First()->MarkDirty();
935 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
939 /* This is called once per day for each vehicle, but not in the first tick of the day */
940 v->OnNewDay();
944 void CallVehicleTicks()
946 _vehicles_to_autoreplace.clear();
948 RunVehicleDayProc();
951 PerformanceMeasurer framerate(PFE_GL_ECONOMY);
952 Station *st;
953 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
955 PerformanceAccumulator::Reset(PFE_GL_TRAINS);
956 PerformanceAccumulator::Reset(PFE_GL_ROADVEHS);
957 PerformanceAccumulator::Reset(PFE_GL_SHIPS);
958 PerformanceAccumulator::Reset(PFE_GL_AIRCRAFT);
960 Vehicle *v;
961 FOR_ALL_VEHICLES(v) {
962 /* Vehicle could be deleted in this tick */
963 if (!v->Tick()) {
964 assert(Vehicle::Get(vehicle_index) == nullptr);
965 continue;
968 assert(Vehicle::Get(vehicle_index) == v);
970 switch (v->type) {
971 default: break;
973 case VEH_TRAIN:
974 case VEH_ROAD:
975 case VEH_AIRCRAFT:
976 case VEH_SHIP: {
977 Vehicle *front = v->First();
979 if (v->vcache.cached_cargo_age_period != 0) {
980 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
981 if (--v->cargo_age_counter == 0) {
982 v->cargo.AgeCargo();
983 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
987 /* Do not play any sound when crashed */
988 if (front->vehstatus & VS_CRASHED) continue;
990 /* Do not play any sound when in depot or tunnel */
991 if (v->vehstatus & VS_HIDDEN) continue;
993 /* Do not play any sound when stopped */
994 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
996 /* Check vehicle type specifics */
997 switch (v->type) {
998 case VEH_TRAIN:
999 if (Train::From(v)->IsWagon()) continue;
1000 break;
1002 case VEH_ROAD:
1003 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1004 break;
1006 case VEH_AIRCRAFT:
1007 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1008 break;
1010 default:
1011 break;
1014 v->motion_counter += front->cur_speed;
1015 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1016 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1018 /* Play an alternating running sound every 16 ticks */
1019 if (GB(v->tick_counter, 0, 4) == 0) {
1020 /* Play running sound when speed > 0 and not braking */
1021 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1022 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1025 break;
1030 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1031 for (auto &it : _vehicles_to_autoreplace) {
1032 v = it.first;
1033 /* Autoreplace needs the current company set as the vehicle owner */
1034 cur_company.Change(v->owner);
1036 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1037 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1038 * they are already leaving the depot again before being replaced. */
1039 if (it.second) v->vehstatus &= ~VS_STOPPED;
1041 /* Store the position of the effect as the vehicle pointer will become invalid later */
1042 int x = v->x_pos;
1043 int y = v->y_pos;
1044 int z = v->z_pos;
1046 const Company *c = Company::Get(_current_company);
1047 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1048 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1049 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1051 if (!IsLocalCompany()) continue;
1053 if (res.Succeeded()) {
1054 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1055 continue;
1058 StringID error_message = res.GetErrorMessage();
1059 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1061 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1063 StringID message;
1064 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1065 message = error_message;
1066 } else {
1067 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1070 SetDParam(0, v->index);
1071 SetDParam(1, error_message);
1072 AddVehicleAdviceNewsItem(message, v->index);
1075 cur_company.Restore();
1079 * Add vehicle sprite for drawing to the screen.
1080 * @param v Vehicle to draw.
1082 static void DoDrawVehicle(const Vehicle *v)
1084 PaletteID pal = PAL_NONE;
1086 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1088 /* Check whether the vehicle shall be transparent due to the game state */
1089 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1091 if (v->type == VEH_EFFECT) {
1092 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1093 * However, transparent smoke and bubbles look weird, so always hide them. */
1094 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1095 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1098 StartSpriteCombine();
1099 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1100 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1101 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1102 AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1103 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1105 EndSpriteCombine();
1109 * Add the vehicle sprites that should be drawn at a part of the screen.
1110 * @param dpi Rectangle being drawn.
1112 void ViewportAddVehicles(DrawPixelInfo *dpi)
1114 /* The bounding rectangle */
1115 const int l = dpi->left;
1116 const int r = dpi->left + dpi->width;
1117 const int t = dpi->top;
1118 const int b = dpi->top + dpi->height;
1120 /* The hash area to scan */
1121 int xl, xu, yl, yu;
1123 if (dpi->width + (MAX_VEHICLE_PIXEL_X * ZOOM_LVL_BASE) < GEN_HASHX_SIZE) {
1124 xl = GEN_HASHX(l - MAX_VEHICLE_PIXEL_X * ZOOM_LVL_BASE);
1125 xu = GEN_HASHX(r);
1126 } else {
1127 /* scan whole hash row */
1128 xl = 0;
1129 xu = GEN_HASHX_MASK;
1132 if (dpi->height + (MAX_VEHICLE_PIXEL_Y * ZOOM_LVL_BASE) < GEN_HASHY_SIZE) {
1133 yl = GEN_HASHY(t - MAX_VEHICLE_PIXEL_Y * ZOOM_LVL_BASE);
1134 yu = GEN_HASHY(b);
1135 } else {
1136 /* scan whole column */
1137 yl = 0;
1138 yu = GEN_HASHY_MASK;
1141 for (int y = yl;; y = (y + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1142 for (int x = xl;; x = (x + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1143 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1145 while (v != nullptr) {
1146 if (!(v->vehstatus & VS_HIDDEN) &&
1147 l <= v->coord.right &&
1148 t <= v->coord.bottom &&
1149 r >= v->coord.left &&
1150 b >= v->coord.top) {
1151 DoDrawVehicle(v);
1153 v = v->hash_viewport_next;
1156 if (x == xu) break;
1159 if (y == yu) break;
1164 * Find the vehicle close to the clicked coordinates.
1165 * @param vp Viewport clicked in.
1166 * @param x X coordinate in the viewport.
1167 * @param y Y coordinate in the viewport.
1168 * @return Closest vehicle, or \c nullptr if none found.
1170 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1172 Vehicle *found = nullptr, *v;
1173 uint dist, best_dist = UINT_MAX;
1175 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return nullptr;
1177 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1178 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1180 FOR_ALL_VEHICLES(v) {
1181 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1182 x >= v->coord.left && x <= v->coord.right &&
1183 y >= v->coord.top && y <= v->coord.bottom) {
1185 dist = max(
1186 abs(((v->coord.left + v->coord.right) >> 1) - x),
1187 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1190 if (dist < best_dist) {
1191 found = v;
1192 best_dist = dist;
1197 return found;
1201 * Decrease the value of a vehicle.
1202 * @param v %Vehicle to devaluate.
1204 void DecreaseVehicleValue(Vehicle *v)
1206 v->value -= v->value >> 8;
1207 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1210 static const byte _breakdown_chance[64] = {
1211 3, 3, 3, 3, 3, 3, 3, 3,
1212 4, 4, 5, 5, 6, 6, 7, 7,
1213 8, 8, 9, 9, 10, 10, 11, 11,
1214 12, 13, 13, 13, 13, 14, 15, 16,
1215 17, 19, 21, 25, 28, 31, 34, 37,
1216 40, 44, 48, 52, 56, 60, 64, 68,
1217 72, 80, 90, 100, 110, 120, 130, 140,
1218 150, 170, 190, 210, 230, 250, 250, 250,
1221 void CheckVehicleBreakdown(Vehicle *v)
1223 int rel, rel_old;
1225 /* decrease reliability */
1226 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
1227 _settings_game.difficulty.vehicle_breakdowns != 0) {
1228 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1229 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1232 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1233 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1234 v->cur_speed < 5 || _game_mode == GM_MENU) {
1235 return;
1238 uint32 r = Random();
1240 /* increase chance of failure */
1241 int chance = v->breakdown_chance + 1;
1242 if (Chance16I(1, 25, r)) chance += 25;
1243 v->breakdown_chance = min(255, chance);
1245 /* calculate reliability value to use in comparison */
1246 rel = v->reliability;
1247 if (v->type == VEH_SHIP) rel += 0x6666;
1249 /* reduced breakdowns? */
1250 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1252 /* check if to break down */
1253 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1254 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1255 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1256 v->breakdown_chance = 0;
1261 * Handle all of the aspects of a vehicle breakdown
1262 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1263 * @return true iff the vehicle is stopped because of a breakdown
1264 * @note This function always returns false for aircraft, since these never stop for breakdowns
1266 bool Vehicle::HandleBreakdown()
1268 /* Possible states for Vehicle::breakdown_ctr
1269 * 0 - vehicle is running normally
1270 * 1 - vehicle is currently broken down
1271 * 2 - vehicle is going to break down now
1272 * >2 - vehicle is counting down to the actual breakdown event */
1273 switch (this->breakdown_ctr) {
1274 case 0:
1275 return false;
1277 case 2:
1278 this->breakdown_ctr = 1;
1280 if (this->breakdowns_since_last_service != 255) {
1281 this->breakdowns_since_last_service++;
1284 if (this->type == VEH_AIRCRAFT) {
1285 /* Aircraft just need this flag, the rest is handled elsewhere */
1286 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1287 } else {
1288 this->cur_speed = 0;
1290 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1291 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1292 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1293 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1294 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1297 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1298 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1299 if (u != nullptr) u->animation_state = this->breakdown_delay * 2;
1303 this->MarkDirty(); // Update graphics after speed is zeroed
1304 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1305 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1307 FALLTHROUGH;
1308 case 1:
1309 /* Aircraft breakdowns end only when arriving at the airport */
1310 if (this->type == VEH_AIRCRAFT) return false;
1312 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1313 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1314 if (--this->breakdown_delay == 0) {
1315 this->breakdown_ctr = 0;
1316 this->MarkDirty();
1317 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1320 return true;
1322 default:
1323 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1324 return false;
1329 * Update age of a vehicle.
1330 * @param v Vehicle to update.
1332 void AgeVehicle(Vehicle *v)
1334 if (v->age < MAX_DAY) {
1335 v->age++;
1336 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1339 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1341 int age = v->age - v->max_age;
1342 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1343 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1344 v->reliability_spd_dec <<= 1;
1347 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1349 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1350 if (v->Previous() != nullptr || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1352 const Company *c = Company::Get(v->owner);
1353 /* Don't warn if a renew is active */
1354 if (c->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1355 /* Don't warn if a replacement is active */
1356 if (EngineHasReplacementForCompany(c, v->engine_type, v->group_id)) return;
1358 StringID str;
1359 if (age == -DAYS_IN_LEAP_YEAR) {
1360 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1361 } else if (age == 0) {
1362 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1363 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1364 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1365 } else {
1366 return;
1369 SetDParam(0, v->index);
1370 AddVehicleAdviceNewsItem(str, v->index);
1374 * Calculates how full a vehicle is.
1375 * @param front The front vehicle of the consist to check.
1376 * @param colour The string to show depending on if we are unloading or loading
1377 * @return A percentage of how full the Vehicle is.
1378 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1379 * if the vehicle is completely empty or full.
1380 * This is useful for both display and conditional orders.
1382 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1384 int count = 0;
1385 int max = 0;
1386 int cars = 0;
1387 int unloading = 0;
1388 bool loading = false;
1390 bool is_loading = front->current_order.IsType(OT_LOADING);
1392 /* The station may be nullptr when the (colour) string does not need to be set. */
1393 const Station *st = Station::GetIfValid(front->last_station_visited);
1394 assert(colour == nullptr || (st != nullptr && is_loading));
1396 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1397 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1399 /* Count up max and used */
1400 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
1401 count += v->cargo.StoredCount();
1402 max += v->cargo_cap;
1403 if (v->cargo_cap != 0 && colour != nullptr) {
1404 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1405 loading |= !order_no_load &&
1406 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1407 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1408 cars++;
1412 if (colour != nullptr) {
1413 if (unloading == 0 && loading) {
1414 *colour = STR_PERCENT_UP;
1415 } else if (unloading == 0 && !loading) {
1416 *colour = STR_PERCENT_NONE;
1417 } else if (cars == unloading || !loading) {
1418 *colour = STR_PERCENT_DOWN;
1419 } else {
1420 *colour = STR_PERCENT_UP_DOWN;
1424 /* Train without capacity */
1425 if (max == 0) return 100;
1427 /* Return the percentage */
1428 if (count * 2 < max) {
1429 /* Less than 50%; round up, so that 0% means really empty. */
1430 return CeilDiv(count * 100, max);
1431 } else {
1432 /* More than 50%; round down, so that 100% means really full. */
1433 return (count * 100) / max;
1438 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1439 * @param v Vehicle that entered a depot.
1441 void VehicleEnterDepot(Vehicle *v)
1443 /* Always work with the front of the vehicle */
1444 assert(v == v->First());
1446 switch (v->type) {
1447 case VEH_TRAIN: {
1448 Train *t = Train::From(v);
1449 SetWindowClassesDirty(WC_TRAINS_LIST);
1450 /* Clear path reservation */
1451 SetDepotReservation(t->tile, false);
1452 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1454 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1455 t->wait_counter = 0;
1456 t->force_proceed = TFP_NONE;
1457 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1458 t->ConsistChanged(CCF_ARRANGE);
1459 break;
1462 case VEH_ROAD:
1463 SetWindowClassesDirty(WC_ROADVEH_LIST);
1464 break;
1466 case VEH_SHIP: {
1467 SetWindowClassesDirty(WC_SHIPS_LIST);
1468 Ship *ship = Ship::From(v);
1469 ship->state = TRACK_BIT_DEPOT;
1470 ship->UpdateCache();
1471 ship->UpdateViewport(true, true);
1472 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1473 break;
1476 case VEH_AIRCRAFT:
1477 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1478 HandleAircraftEnterHangar(Aircraft::From(v));
1479 break;
1480 default: NOT_REACHED();
1482 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1484 if (v->type != VEH_TRAIN) {
1485 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1486 * 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 */
1487 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1489 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1491 v->vehstatus |= VS_HIDDEN;
1492 v->cur_speed = 0;
1494 VehicleServiceInDepot(v);
1496 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1497 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1498 v->MarkDirty();
1500 InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
1502 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1503 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1505 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1507 /* Test whether we are heading for this depot. If not, do nothing.
1508 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1509 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1510 real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1511 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1512 /* We are heading for another depot, keep driving. */
1513 return;
1516 if (v->current_order.IsRefit()) {
1517 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1518 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1519 cur_company.Restore();
1521 if (cost.Failed()) {
1522 _vehicles_to_autoreplace[v] = false;
1523 if (v->owner == _local_company) {
1524 /* Notify the user that we stopped the vehicle */
1525 SetDParam(0, v->index);
1526 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1528 } else if (cost.GetCost() != 0) {
1529 v->profit_this_year -= cost.GetCost() << 8;
1530 if (v->owner == _local_company) {
1531 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1536 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1537 /* Part of orders */
1538 v->DeleteUnreachedImplicitOrders();
1539 UpdateVehicleTimetable(v, true);
1540 v->IncrementImplicitOrderIndex();
1542 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1543 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1544 _vehicles_to_autoreplace[v] = false;
1545 /* Invalidate last_loading_station. As the link from the station
1546 * before the stop to the station after the stop can't be predicted
1547 * we shouldn't construct it when the vehicle visits the next stop. */
1548 v->last_loading_station = INVALID_STATION;
1549 if (v->owner == _local_company) {
1550 SetDParam(0, v->index);
1551 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1553 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1555 v->current_order.MakeDummy();
1561 * Update the position of the vehicle. This will update the hash that tells
1562 * which vehicles are on a tile.
1564 void Vehicle::UpdatePosition()
1566 UpdateVehicleTileHash(this, false);
1570 * Update the vehicle on the viewport, updating the right hash and setting the
1571 * new coordinates.
1572 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1574 void Vehicle::UpdateViewport(bool dirty)
1576 Rect new_coord;
1577 this->sprite_seq.GetBounds(&new_coord);
1579 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1580 new_coord.left += pt.x;
1581 new_coord.top += pt.y;
1582 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1583 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1585 UpdateVehicleViewportHash(this, new_coord.left, new_coord.top);
1587 Rect old_coord = this->coord;
1588 this->coord = new_coord;
1590 if (dirty) {
1591 if (old_coord.left == INVALID_COORD) {
1592 this->MarkAllViewportsDirty();
1593 } else {
1594 ::MarkAllViewportsDirty(
1595 min(old_coord.left, this->coord.left),
1596 min(old_coord.top, this->coord.top),
1597 max(old_coord.right, this->coord.right),
1598 max(old_coord.bottom, this->coord.bottom));
1604 * Update the position of the vehicle, and update the viewport.
1606 void Vehicle::UpdatePositionAndViewport()
1608 this->UpdatePosition();
1609 this->UpdateViewport(true);
1613 * Marks viewports dirty where the vehicle's image is.
1615 void Vehicle::MarkAllViewportsDirty() const
1617 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1621 * Get position information of a vehicle when moving one pixel in the direction it is facing
1622 * @param v Vehicle to move
1623 * @return Position information after the move
1625 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1627 static const int8 _delta_coord[16] = {
1628 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1629 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1632 int x = v->x_pos + _delta_coord[v->direction];
1633 int y = v->y_pos + _delta_coord[v->direction + 8];
1635 GetNewVehiclePosResult gp;
1636 gp.x = x;
1637 gp.y = y;
1638 gp.old_tile = v->tile;
1639 gp.new_tile = TileVirtXY(x, y);
1640 return gp;
1643 static const Direction _new_direction_table[] = {
1644 DIR_N, DIR_NW, DIR_W,
1645 DIR_NE, DIR_SE, DIR_SW,
1646 DIR_E, DIR_SE, DIR_S
1649 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1651 int i = 0;
1653 if (y >= v->y_pos) {
1654 if (y != v->y_pos) i += 3;
1655 i += 3;
1658 if (x >= v->x_pos) {
1659 if (x != v->x_pos) i++;
1660 i++;
1663 Direction dir = v->direction;
1665 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1666 if (dirdiff == DIRDIFF_SAME) return dir;
1667 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1671 * Call the tile callback function for a vehicle entering a tile
1672 * @param v Vehicle entering the tile
1673 * @param tile Tile entered
1674 * @param x X position
1675 * @param y Y position
1676 * @return Some meta-data over the to be entered tile.
1677 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1679 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1681 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1685 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1686 * struct initialization, except after each call to this->NextID() the returned value
1687 * is assigned to a vehicle.
1688 * @param type type of vehicle
1689 * @param owner owner of vehicles
1691 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(nullptr), maxid(0), curid(0)
1693 /* Find maximum */
1694 const Vehicle *v;
1695 FOR_ALL_VEHICLES(v) {
1696 if (v->type == type && v->owner == owner) {
1697 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1701 if (this->maxid == 0) return;
1703 /* Reserving 'maxid + 2' because we need:
1704 * - space for the last item (with v->unitnumber == maxid)
1705 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1706 this->cache = CallocT<bool>(this->maxid + 2);
1708 /* Fill the cache */
1709 FOR_ALL_VEHICLES(v) {
1710 if (v->type == type && v->owner == owner) {
1711 this->cache[v->unitnumber] = true;
1716 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1717 UnitID FreeUnitIDGenerator::NextID()
1719 if (this->maxid <= this->curid) return ++this->curid;
1721 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1723 return this->curid;
1727 * Get an unused unit number for a vehicle (if allowed).
1728 * @param type Type of vehicle
1729 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1731 UnitID GetFreeUnitNumber(VehicleType type)
1733 /* Check whether it is allowed to build another vehicle. */
1734 uint max_veh;
1735 switch (type) {
1736 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1737 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1738 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1739 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1740 default: NOT_REACHED();
1743 const Company *c = Company::Get(_current_company);
1744 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1746 FreeUnitIDGenerator gen(type, _current_company);
1748 return gen.NextID();
1753 * Check whether we can build infrastructure for the given
1754 * vehicle type. This to disable building stations etc. when
1755 * you are not allowed/able to have the vehicle type yet.
1756 * @param type the vehicle type to check this for
1757 * @return true if there is any reason why you may build
1758 * the infrastructure for the given vehicle type
1760 bool CanBuildVehicleInfrastructure(VehicleType type)
1762 assert(IsCompanyBuildableVehicleType(type));
1764 if (!Company::IsValidID(_local_company)) return false;
1765 if (!_settings_client.gui.disable_unsuitable_building) return true;
1767 UnitID max;
1768 switch (type) {
1769 case VEH_TRAIN:
1770 if (!HasAnyRailtypesAvail(_local_company)) return false;
1771 max = _settings_game.vehicle.max_trains;
1772 break;
1773 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1774 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1775 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1776 default: NOT_REACHED();
1779 /* We can build vehicle infrastructure when we may build the vehicle type */
1780 if (max > 0) {
1781 /* Can we actually build the vehicle type? */
1782 const Engine *e;
1783 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1784 if (HasBit(e->company_avail, _local_company)) return true;
1786 return false;
1789 /* We should be able to build infrastructure when we have the actual vehicle type */
1790 const Vehicle *v;
1791 FOR_ALL_VEHICLES(v) {
1792 if (v->owner == _local_company && v->type == type) return true;
1795 return false;
1800 * Determines the #LiveryScheme for a vehicle.
1801 * @param engine_type Engine of the vehicle.
1802 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1803 * @param v the vehicle, \c nullptr if in purchase list etc.
1804 * @return livery scheme to use.
1806 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1808 CargoID cargo_type = v == nullptr ? (CargoID)CT_INVALID : v->cargo_type;
1809 const Engine *e = Engine::Get(engine_type);
1810 switch (e->type) {
1811 default: NOT_REACHED();
1812 case VEH_TRAIN:
1813 if (v != nullptr && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1814 /* Wagonoverrides use the colour scheme of the front engine.
1815 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1816 engine_type = parent_engine_type;
1817 e = Engine::Get(engine_type);
1818 /* Note: Luckily cargo_type is not needed for engines */
1821 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1822 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1823 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1824 if (!CargoSpec::Get(cargo_type)->is_freight) {
1825 if (parent_engine_type == INVALID_ENGINE) {
1826 return LS_PASSENGER_WAGON_STEAM;
1827 } else {
1828 bool is_mu = HasBit(EngInfo(parent_engine_type)->misc_flags, EF_RAIL_IS_MU);
1829 switch (RailVehInfo(parent_engine_type)->engclass) {
1830 default: NOT_REACHED();
1831 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1832 case EC_DIESEL: return is_mu ? LS_DMU : LS_PASSENGER_WAGON_DIESEL;
1833 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_PASSENGER_WAGON_ELECTRIC;
1834 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1835 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1838 } else {
1839 return LS_FREIGHT_WAGON;
1841 } else {
1842 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1844 switch (e->u.rail.engclass) {
1845 default: NOT_REACHED();
1846 case EC_STEAM: return LS_STEAM;
1847 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1848 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1849 case EC_MONORAIL: return LS_MONORAIL;
1850 case EC_MAGLEV: return LS_MAGLEV;
1854 case VEH_ROAD:
1855 /* Always use the livery of the front */
1856 if (v != nullptr && parent_engine_type != INVALID_ENGINE) {
1857 engine_type = parent_engine_type;
1858 e = Engine::Get(engine_type);
1859 cargo_type = v->First()->cargo_type;
1861 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1862 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1864 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1865 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1866 /* Tram */
1867 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1868 } else {
1869 /* Bus or truck */
1870 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1873 case VEH_SHIP:
1874 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1875 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1876 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1878 case VEH_AIRCRAFT:
1879 switch (e->u.air.subtype) {
1880 case AIR_HELI: return LS_HELICOPTER;
1881 case AIR_CTOL: return LS_SMALL_PLANE;
1882 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1883 default: NOT_REACHED();
1889 * Determines the livery for a vehicle.
1890 * @param engine_type EngineID of the vehicle
1891 * @param company Owner of the vehicle
1892 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1893 * @param v the vehicle. nullptr if in purchase list etc.
1894 * @param livery_setting The livery settings to use for acquiring the livery information.
1895 * @return livery to use
1897 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1899 const Company *c = Company::Get(company);
1900 LiveryScheme scheme = LS_DEFAULT;
1902 if (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company)) {
1903 if (v != nullptr) {
1904 const Group *g = Group::GetIfValid(v->First()->group_id);
1905 if (g != nullptr) {
1906 /* Traverse parents until we find a livery or reach the top */
1907 while (g->livery.in_use == 0 && g->parent != INVALID_GROUP) {
1908 g = Group::Get(g->parent);
1910 if (g->livery.in_use != 0) return &g->livery;
1914 /* The default livery is always available for use, but its in_use flag determines
1915 * whether any _other_ liveries are in use. */
1916 if (c->livery[LS_DEFAULT].in_use != 0) {
1917 /* Determine the livery scheme to use */
1918 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1922 return &c->livery[scheme];
1926 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1928 PaletteID map = (v != nullptr) ? v->colourmap : PAL_NONE;
1930 /* Return cached value if any */
1931 if (map != PAL_NONE) return map;
1933 const Engine *e = Engine::Get(engine_type);
1935 /* Check if we should use the colour map callback */
1936 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1937 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1938 /* Failure means "use the default two-colour" */
1939 if (callback != CALLBACK_FAILED) {
1940 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1941 map = GB(callback, 0, 14);
1942 /* If bit 14 is set, then the company colours are applied to the
1943 * map else it's returned as-is. */
1944 if (!HasBit(callback, 14)) {
1945 /* Update cache */
1946 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
1947 return map;
1952 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1954 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1956 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1957 if (!Company::IsValidID(company)) return map;
1959 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1961 map += livery->colour1;
1962 if (twocc) map += livery->colour2 * 16;
1964 /* Update cache */
1965 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
1966 return map;
1970 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
1971 * @param engine_type ID of engine
1972 * @param company ID of company
1973 * @return A ready-to-use palette modifier
1975 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1977 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, nullptr);
1981 * Get the colour map for a vehicle.
1982 * @param v Vehicle to get colour map for
1983 * @return A ready-to-use palette modifier
1985 PaletteID GetVehiclePalette(const Vehicle *v)
1987 if (v->IsGroundVehicle()) {
1988 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1991 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1995 * Delete all implicit orders which were not reached.
1997 void Vehicle::DeleteUnreachedImplicitOrders()
1999 if (this->IsGroundVehicle()) {
2000 uint16 &gv_flags = this->GetGroundVehicleFlags();
2001 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2002 /* Do not delete orders, only skip them */
2003 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2004 this->cur_implicit_order_index = this->cur_real_order_index;
2005 InvalidateVehicleOrder(this, 0);
2006 return;
2010 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2011 while (order != nullptr) {
2012 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2014 if (order->IsType(OT_IMPLICIT)) {
2015 DeleteOrder(this, this->cur_implicit_order_index);
2016 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2017 order = this->GetOrder(this->cur_implicit_order_index);
2018 } else {
2019 /* Skip non-implicit orders, e.g. service-orders */
2020 order = order->next;
2021 this->cur_implicit_order_index++;
2024 /* Wrap around */
2025 if (order == nullptr) {
2026 order = this->GetOrder(0);
2027 this->cur_implicit_order_index = 0;
2033 * Prepare everything to begin the loading when arriving at a station.
2034 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2036 void Vehicle::BeginLoading()
2038 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2040 if (this->current_order.IsType(OT_GOTO_STATION) &&
2041 this->current_order.GetDestination() == this->last_station_visited) {
2042 this->DeleteUnreachedImplicitOrders();
2044 /* Now both order indices point to the destination station, and we can start loading */
2045 this->current_order.MakeLoading(true);
2046 UpdateVehicleTimetable(this, true);
2048 /* Furthermore add the Non Stop flag to mark that this station
2049 * is the actual destination of the vehicle, which is (for example)
2050 * necessary to be known for HandleTrainLoading to determine
2051 * whether the train is lost or not; not marking a train lost
2052 * that arrives at random stations is bad. */
2053 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2055 } else {
2056 /* We weren't scheduled to stop here. Insert an implicit order
2057 * to show that we are stopping here.
2058 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2059 * the 'wrong' terminal when skipping orders etc. */
2060 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2061 if (this->IsGroundVehicle() &&
2062 (in_list == nullptr || !in_list->IsType(OT_IMPLICIT) ||
2063 in_list->GetDestination() != this->last_station_visited)) {
2064 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2065 /* Do not create consecutive duplicates of implicit orders */
2066 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2067 if (prev_order == nullptr ||
2068 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2069 prev_order->GetDestination() != this->last_station_visited) {
2071 /* Prefer deleting implicit orders instead of inserting new ones,
2072 * so test whether the right order follows later. In case of only
2073 * implicit orders treat the last order in the list like an
2074 * explicit one, except if the overall number of orders surpasses
2075 * IMPLICIT_ORDER_ONLY_CAP. */
2076 int target_index = this->cur_implicit_order_index;
2077 bool found = false;
2078 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2079 const Order *order = this->GetOrder(target_index);
2080 if (order == nullptr) break; // No orders.
2081 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2082 found = true;
2083 break;
2085 target_index++;
2086 if (target_index >= this->orders.list->GetNumOrders()) {
2087 if (this->GetNumManualOrders() == 0 &&
2088 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2089 break;
2091 target_index = 0;
2093 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2096 if (found) {
2097 if (suppress_implicit_orders) {
2098 /* Skip to the found order */
2099 this->cur_implicit_order_index = target_index;
2100 InvalidateVehicleOrder(this, 0);
2101 } else {
2102 /* Delete all implicit orders up to the station we just reached */
2103 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2104 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2105 if (order->IsType(OT_IMPLICIT)) {
2106 DeleteOrder(this, this->cur_implicit_order_index);
2107 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2108 order = this->GetOrder(this->cur_implicit_order_index);
2109 } else {
2110 /* Skip non-implicit orders, e.g. service-orders */
2111 order = order->next;
2112 this->cur_implicit_order_index++;
2115 /* Wrap around */
2116 if (order == nullptr) {
2117 order = this->GetOrder(0);
2118 this->cur_implicit_order_index = 0;
2120 assert(order != nullptr);
2123 } else if (!suppress_implicit_orders &&
2124 ((this->orders.list == nullptr ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2125 Order::CanAllocateItem()) {
2126 /* Insert new implicit order */
2127 Order *implicit_order = new Order();
2128 implicit_order->MakeImplicit(this->last_station_visited);
2129 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2130 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2132 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2133 * Reenable it for this vehicle */
2134 uint16 &gv_flags = this->GetGroundVehicleFlags();
2135 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2139 this->current_order.MakeLoading(false);
2142 if (this->last_loading_station != INVALID_STATION &&
2143 this->last_loading_station != this->last_station_visited &&
2144 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2145 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2146 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2149 PrepareUnload(this);
2151 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2152 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2153 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2154 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2156 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2157 this->cur_speed = 0;
2158 this->MarkDirty();
2162 * Return all reserved cargo packets to the station and reset all packets
2163 * staged for transfer.
2164 * @param st the station where the reserved packets should go.
2166 void Vehicle::CancelReservation(StationID next, Station *st)
2168 for (Vehicle *v = this; v != nullptr; v = v->next) {
2169 VehicleCargoList &cargo = v->cargo;
2170 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2171 DEBUG(misc, 1, "cancelling cargo reservation");
2172 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2173 cargo.SetTransferLoadPlace(st->xy);
2175 cargo.KeepAll();
2180 * Perform all actions when leaving a station.
2181 * @pre this->current_order.IsType(OT_LOADING)
2183 void Vehicle::LeaveStation()
2185 assert(this->current_order.IsType(OT_LOADING));
2187 delete this->cargo_payment;
2188 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
2190 /* Only update the timetable if the vehicle was supposed to stop here. */
2191 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2193 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2194 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2195 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2196 /* Refresh next hop stats to make sure we've done that at least once
2197 * during the stop and that refit_cap == cargo_cap for each vehicle in
2198 * the consist. */
2199 this->ResetRefitCaps();
2200 LinkRefresher::Run(this);
2202 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2203 this->last_loading_station = this->last_station_visited;
2204 } else {
2205 /* if the vehicle couldn't load and had to unload or transfer everything
2206 * set the last loading station to invalid as it will leave empty. */
2207 this->last_loading_station = INVALID_STATION;
2211 this->current_order.MakeLeaveStation();
2212 Station *st = Station::Get(this->last_station_visited);
2213 this->CancelReservation(INVALID_STATION, st);
2214 st->loading_vehicles.remove(this);
2216 HideFillingPercent(&this->fill_percent_te_id);
2217 trip_occupancy = CalcPercentVehicleFilled(this, nullptr);
2219 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2220 /* Trigger station animation (trains only) */
2221 if (IsTileType(this->tile, MP_STATION)) {
2222 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2223 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2226 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2229 this->MarkDirty();
2233 * Reset all refit_cap in the consist to cargo_cap.
2235 void Vehicle::ResetRefitCaps()
2237 for (Vehicle *v = this; v != nullptr; v = v->Next()) v->refit_cap = v->cargo_cap;
2241 * Handle the loading of the vehicle; when not it skips through dummy
2242 * orders and does nothing in all other cases.
2243 * @param mode is the non-first call for this vehicle in this tick?
2245 void Vehicle::HandleLoading(bool mode)
2247 switch (this->current_order.GetType()) {
2248 case OT_LOADING: {
2249 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2251 /* Not the first call for this tick, or still loading */
2252 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2254 this->PlayLeaveStationSound();
2256 this->LeaveStation();
2258 /* Only advance to next order if we just loaded at the current one */
2259 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2260 if (order == nullptr ||
2261 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2262 order->GetDestination() != this->last_station_visited) {
2263 return;
2265 break;
2268 case OT_DUMMY: break;
2270 default: return;
2273 this->IncrementImplicitOrderIndex();
2277 * Get a map of cargoes and free capacities in the consist.
2278 * @param capacities Map to be filled with cargoes and capacities.
2280 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2282 for (const Vehicle *v = this; v != nullptr; v = v->Next()) {
2283 if (v->cargo_cap == 0) continue;
2284 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2285 if (pair == capacities.End()) {
2286 capacities.push_back({v->cargo_type, v->cargo_cap - v->cargo.StoredCount()});
2287 } else {
2288 pair->second += v->cargo_cap - v->cargo.StoredCount();
2293 uint Vehicle::GetConsistTotalCapacity() const
2295 uint result = 0;
2296 for (const Vehicle *v = this; v != nullptr; v = v->Next()) {
2297 result += v->cargo_cap;
2299 return result;
2303 * Send this vehicle to the depot using the given command(s).
2304 * @param flags the command flags (like execute and such).
2305 * @param command the command to execute.
2306 * @return the cost of the depot action.
2308 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2310 CommandCost ret = CheckOwnership(this->owner);
2311 if (ret.Failed()) return ret;
2313 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2314 if (this->IsStoppedInDepot()) return CMD_ERROR;
2316 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2317 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2318 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2319 /* We called with a different DEPOT_SERVICE setting.
2320 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2321 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2322 if (flags & DC_EXEC) {
2323 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2324 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2325 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2327 return CommandCost();
2330 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2331 if (flags & DC_EXEC) {
2332 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2333 * then skip to the next order; effectively cancelling this forced service */
2334 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2336 if (this->IsGroundVehicle()) {
2337 uint16 &gv_flags = this->GetGroundVehicleFlags();
2338 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2341 this->current_order.MakeDummy();
2342 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2344 return CommandCost();
2347 TileIndex location;
2348 DestinationID destination;
2349 bool reverse;
2350 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};
2351 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2353 if (flags & DC_EXEC) {
2354 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2356 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2357 uint16 &gv_flags = this->GetGroundVehicleFlags();
2358 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2361 this->SetDestTile(location);
2362 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2363 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2364 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2366 /* If there is no depot in front and the train is not already reversing, reverse automatically (trains only) */
2367 if (this->type == VEH_TRAIN && (reverse ^ HasBit(Train::From(this)->flags, VRF_REVERSING))) {
2368 DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2371 if (this->type == VEH_AIRCRAFT) {
2372 Aircraft *a = Aircraft::From(this);
2373 if (a->state == FLYING && a->targetairport != destination) {
2374 /* The aircraft is now heading for a different hangar than the next in the orders */
2375 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2376 AircraftNextAirportPos_and_Order(a);
2381 return CommandCost();
2386 * Update the cached visual effect.
2387 * @param allow_power_change true if the wagon-is-powered-state may change.
2389 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2391 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2392 const Engine *e = this->GetEngine();
2394 /* Evaluate properties */
2395 byte visual_effect;
2396 switch (e->type) {
2397 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2398 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2399 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2400 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2403 /* Check powered wagon / visual effect callback */
2404 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2405 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2407 if (callback != CALLBACK_FAILED) {
2408 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2410 callback = GB(callback, 0, 8);
2411 /* Avoid accidentally setting 'visual_effect' to the default value
2412 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2413 if (callback == VE_DEFAULT) {
2414 assert(HasBit(callback, VE_DISABLE_EFFECT));
2415 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2417 visual_effect = callback;
2421 /* Apply default values */
2422 if (visual_effect == VE_DEFAULT ||
2423 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2424 /* Only train engines have default effects.
2425 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2426 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2427 if (visual_effect == VE_DEFAULT) {
2428 visual_effect = 1 << VE_DISABLE_EFFECT;
2429 } else {
2430 SetBit(visual_effect, VE_DISABLE_EFFECT);
2432 } else {
2433 if (visual_effect == VE_DEFAULT) {
2434 /* Also set the offset */
2435 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2437 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2441 this->vcache.cached_vis_effect = visual_effect;
2443 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2444 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2445 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2449 static const int8 _vehicle_smoke_pos[8] = {
2450 1, 1, 1, 0, -1, -1, -1, 0
2454 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2455 * @param v Vehicle to create effects for.
2457 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2459 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2460 if (callback == CALLBACK_FAILED) return;
2462 uint count = GB(callback, 0, 2);
2463 bool auto_center = HasBit(callback, 13);
2464 bool auto_rotate = !HasBit(callback, 14);
2466 int8 l_center = 0;
2467 if (auto_center) {
2468 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2469 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2470 } else {
2471 /* For trains: Compute offset from vehicle position to sprite position */
2472 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2475 Direction l_dir = v->direction;
2476 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2477 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2479 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2480 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2482 for (uint i = 0; i < count; i++) {
2483 uint32 reg = GetRegister(0x100 + i);
2484 uint type = GB(reg, 0, 8);
2485 int8 x = GB(reg, 8, 8);
2486 int8 y = GB(reg, 16, 8);
2487 int8 z = GB(reg, 24, 8);
2489 if (auto_rotate) {
2490 int8 l = x;
2491 int8 t = y;
2492 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2493 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2496 if (type >= 0xF0) {
2497 switch (type) {
2498 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2499 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2500 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2501 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2502 default: break;
2509 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2510 * @pre this->IsPrimaryVehicle()
2512 void Vehicle::ShowVisualEffect() const
2514 assert(this->IsPrimaryVehicle());
2515 bool sound = false;
2517 /* Do not show any smoke when:
2518 * - vehicle smoke is disabled by the player
2519 * - the vehicle is slowing down or stopped (by the player)
2520 * - the vehicle is moving very slowly
2522 if (_settings_game.vehicle.smoke_amount == 0 ||
2523 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2524 this->cur_speed < 2) {
2525 return;
2528 /* Use the speed as limited by underground and orders. */
2529 uint max_speed = this->GetCurrentMaxSpeed();
2531 if (this->type == VEH_TRAIN) {
2532 const Train *t = Train::From(this);
2533 /* For trains, do not show any smoke when:
2534 * - the train is reversing
2535 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2537 if (HasBit(t->flags, VRF_REVERSING) ||
2538 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2539 t->cur_speed >= max_speed)) {
2540 return;
2544 const Vehicle *v = this;
2546 do {
2547 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2548 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2549 VisualEffectSpawnModel effect_model = VESM_NONE;
2550 if (advanced) {
2551 effect_offset = VE_OFFSET_CENTRE;
2552 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2553 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2554 } else {
2555 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2556 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2557 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2558 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2559 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2562 /* Show no smoke when:
2563 * - Smoke has been disabled for this vehicle
2564 * - The vehicle is not visible
2565 * - The vehicle is under a bridge
2566 * - The vehicle is on a depot tile
2567 * - The vehicle is on a tunnel tile
2568 * - The vehicle is a train engine that is currently unpowered */
2569 if (effect_model == VESM_NONE ||
2570 v->vehstatus & VS_HIDDEN ||
2571 IsBridgeAbove(v->tile) ||
2572 IsDepotTile(v->tile) ||
2573 IsTunnelTile(v->tile) ||
2574 (v->type == VEH_TRAIN &&
2575 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2576 continue;
2579 EffectVehicleType evt = EV_END;
2580 switch (effect_model) {
2581 case VESM_STEAM:
2582 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2583 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2584 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2585 * REGULATION:
2586 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2587 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2588 evt = EV_STEAM_SMOKE;
2590 break;
2592 case VESM_DIESEL: {
2593 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2594 * when smoke emission stops.
2595 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2596 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2597 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2598 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2599 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2600 * maximum speed no diesel_smoke is emitted.
2601 * REGULATION:
2602 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2603 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2604 int power_weight_effect = 0;
2605 if (v->type == VEH_TRAIN) {
2606 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2608 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2609 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2610 evt = EV_DIESEL_SMOKE;
2612 break;
2615 case VESM_ELECTRIC:
2616 /* Electric train's spark - more often occurs when train is departing (more load)
2617 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2618 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2619 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2620 * REGULATION:
2621 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2622 if (GB(v->tick_counter, 0, 2) == 0 &&
2623 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2624 evt = EV_ELECTRIC_SPARK;
2626 break;
2628 default:
2629 NOT_REACHED();
2632 if (evt != EV_END && advanced) {
2633 sound = true;
2634 SpawnAdvancedVisualEffect(v);
2635 } else if (evt != EV_END) {
2636 sound = true;
2638 /* The effect offset is relative to a point 4 units behind the vehicle's
2639 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2640 * correction factor. */
2641 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2643 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2644 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2646 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2647 x = -x;
2648 y = -y;
2651 CreateEffectVehicleRel(v, x, y, 10, evt);
2653 } while ((v = v->Next()) != nullptr);
2655 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2659 * Set the next vehicle of this vehicle.
2660 * @param next the next vehicle. nullptr removes the next vehicle.
2662 void Vehicle::SetNext(Vehicle *next)
2664 assert(this != next);
2666 if (this->next != nullptr) {
2667 /* We had an old next vehicle. Update the first and previous pointers */
2668 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2669 v->first = this->next;
2671 this->next->previous = nullptr;
2674 this->next = next;
2676 if (this->next != nullptr) {
2677 /* A new next vehicle. Update the first and previous pointers */
2678 if (this->next->previous != nullptr) this->next->previous->next = nullptr;
2679 this->next->previous = this;
2680 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2681 v->first = this->first;
2687 * Adds this vehicle to a shared vehicle chain.
2688 * @param shared_chain a vehicle of the chain with shared vehicles.
2689 * @pre !this->IsOrderListShared()
2691 void Vehicle::AddToShared(Vehicle *shared_chain)
2693 assert(this->previous_shared == nullptr && this->next_shared == nullptr);
2695 if (shared_chain->orders.list == nullptr) {
2696 assert(shared_chain->previous_shared == nullptr);
2697 assert(shared_chain->next_shared == nullptr);
2698 this->orders.list = shared_chain->orders.list = new OrderList(nullptr, shared_chain);
2701 this->next_shared = shared_chain->next_shared;
2702 this->previous_shared = shared_chain;
2704 shared_chain->next_shared = this;
2706 if (this->next_shared != nullptr) this->next_shared->previous_shared = this;
2708 shared_chain->orders.list->AddVehicle(this);
2712 * Removes the vehicle from the shared order list.
2714 void Vehicle::RemoveFromShared()
2716 /* Remember if we were first and the old window number before RemoveVehicle()
2717 * as this changes first if needed. */
2718 bool were_first = (this->FirstShared() == this);
2719 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2721 this->orders.list->RemoveVehicle(this);
2723 if (!were_first) {
2724 /* We are not the first shared one, so only relink our previous one. */
2725 this->previous_shared->next_shared = this->NextShared();
2728 if (this->next_shared != nullptr) this->next_shared->previous_shared = this->previous_shared;
2731 if (this->orders.list->GetNumVehicles() == 1) {
2732 /* When there is only one vehicle, remove the shared order list window. */
2733 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2734 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
2735 } else if (were_first) {
2736 /* If we were the first one, update to the new first one.
2737 * Note: FirstShared() is already the new first */
2738 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2741 this->next_shared = nullptr;
2742 this->previous_shared = nullptr;
2745 void VehiclesYearlyLoop()
2747 Vehicle *v;
2748 FOR_ALL_VEHICLES(v) {
2749 if (v->IsPrimaryVehicle()) {
2750 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2751 Money profit = v->GetDisplayProfitThisYear();
2752 if (v->age >= 730 && profit < 0) {
2753 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2754 SetDParam(0, v->index);
2755 SetDParam(1, profit);
2756 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2758 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2761 v->profit_last_year = v->profit_this_year;
2762 v->profit_this_year = 0;
2763 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2766 GroupStatistics::UpdateProfits();
2767 SetWindowClassesDirty(WC_TRAINS_LIST);
2768 SetWindowClassesDirty(WC_SHIPS_LIST);
2769 SetWindowClassesDirty(WC_ROADVEH_LIST);
2770 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2775 * Can this station be used by the given engine type?
2776 * @param engine_type the type of vehicles to test
2777 * @param st the station to test for
2778 * @return true if and only if the vehicle of the type can use this station.
2779 * @note For road vehicles the Vehicle is needed to determine whether it can
2780 * use the station. This function will return true for road vehicles
2781 * when at least one of the facilities is available.
2783 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2785 const Engine *e = Engine::GetIfValid(engine_type);
2786 assert(e != nullptr);
2788 switch (e->type) {
2789 case VEH_TRAIN:
2790 return (st->facilities & FACIL_TRAIN) != 0;
2792 case VEH_ROAD:
2793 /* For road vehicles we need the vehicle to know whether it can actually
2794 * use the station, but if it doesn't have facilities for RVs it is
2795 * certainly not possible that the station can be used. */
2796 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2798 case VEH_SHIP:
2799 return (st->facilities & FACIL_DOCK) != 0;
2801 case VEH_AIRCRAFT:
2802 return (st->facilities & FACIL_AIRPORT) != 0 &&
2803 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2805 default:
2806 return false;
2811 * Can this station be used by the given vehicle?
2812 * @param v the vehicle to test
2813 * @param st the station to test for
2814 * @return true if and only if the vehicle can use this station.
2816 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2818 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != nullptr;
2820 return CanVehicleUseStation(v->engine_type, st);
2824 * Access the ground vehicle cache of the vehicle.
2825 * @pre The vehicle is a #GroundVehicle.
2826 * @return #GroundVehicleCache of the vehicle.
2828 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2830 assert(this->IsGroundVehicle());
2831 if (this->type == VEH_TRAIN) {
2832 return &Train::From(this)->gcache;
2833 } else {
2834 return &RoadVehicle::From(this)->gcache;
2839 * Access the ground vehicle cache of the vehicle.
2840 * @pre The vehicle is a #GroundVehicle.
2841 * @return #GroundVehicleCache of the vehicle.
2843 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2845 assert(this->IsGroundVehicle());
2846 if (this->type == VEH_TRAIN) {
2847 return &Train::From(this)->gcache;
2848 } else {
2849 return &RoadVehicle::From(this)->gcache;
2854 * Access the ground vehicle flags of the vehicle.
2855 * @pre The vehicle is a #GroundVehicle.
2856 * @return #GroundVehicleFlags of the vehicle.
2858 uint16 &Vehicle::GetGroundVehicleFlags()
2860 assert(this->IsGroundVehicle());
2861 if (this->type == VEH_TRAIN) {
2862 return Train::From(this)->gv_flags;
2863 } else {
2864 return RoadVehicle::From(this)->gv_flags;
2869 * Access the ground vehicle flags of the vehicle.
2870 * @pre The vehicle is a #GroundVehicle.
2871 * @return #GroundVehicleFlags of the vehicle.
2873 const uint16 &Vehicle::GetGroundVehicleFlags() const
2875 assert(this->IsGroundVehicle());
2876 if (this->type == VEH_TRAIN) {
2877 return Train::From(this)->gv_flags;
2878 } else {
2879 return RoadVehicle::From(this)->gv_flags;
2884 * Calculates the set of vehicles that will be affected by a given selection.
2885 * @param[in,out] set Set of affected vehicles.
2886 * @param v First vehicle of the selection.
2887 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2888 * @pre \a set must be empty.
2889 * @post \a set will contain the vehicles that will be refitted.
2891 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2893 if (v->type == VEH_TRAIN) {
2894 Train *u = Train::From(v);
2895 /* Only include whole vehicles, so start with the first articulated part */
2896 u = u->GetFirstEnginePart();
2898 /* Include num_vehicles vehicles, not counting articulated parts */
2899 for (; u != nullptr && num_vehicles > 0; num_vehicles--) {
2900 do {
2901 /* Include current vehicle in the selection. */
2902 include(set, u->index);
2904 /* If the vehicle is multiheaded, add the other part too. */
2905 if (u->IsMultiheaded()) include(set, u->other_multiheaded_part->index);
2907 u = u->Next();
2908 } while (u != nullptr && u->IsArticulatedPart());