Fix: Don't allow right-click to close world generation progress window. (#13084)
[openttd-github.git] / src / vehicle.cpp
blob7e5e63bc300078d1fdf6399db4d65d6a021301ac
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file vehicle.cpp Base implementations of all vehicles. */
10 #include "stdafx.h"
11 #include "error.h"
12 #include "roadveh.h"
13 #include "ship.h"
14 #include "spritecache.h"
15 #include "timetable.h"
16 #include "viewport_func.h"
17 #include "news_func.h"
18 #include "command_func.h"
19 #include "company_func.h"
20 #include "train.h"
21 #include "aircraft.h"
22 #include "newgrf_debug.h"
23 #include "newgrf_sound.h"
24 #include "newgrf_station.h"
25 #include "group_gui.h"
26 #include "strings_func.h"
27 #include "zoom_func.h"
28 #include "vehicle_func.h"
29 #include "autoreplace_func.h"
30 #include "autoreplace_gui.h"
31 #include "station_base.h"
32 #include "ai/ai.hpp"
33 #include "depot_func.h"
34 #include "network/network.h"
35 #include "core/pool_func.hpp"
36 #include "economy_base.h"
37 #include "articulated_vehicles.h"
38 #include "roadstop_base.h"
39 #include "core/random_func.hpp"
40 #include "core/backup_type.hpp"
41 #include "core/container_func.hpp"
42 #include "order_backup.h"
43 #include "sound_func.h"
44 #include "effectvehicle_func.h"
45 #include "effectvehicle_base.h"
46 #include "vehiclelist.h"
47 #include "bridge_map.h"
48 #include "tunnel_map.h"
49 #include "depot_map.h"
50 #include "gamelog.h"
51 #include "linkgraph/linkgraph.h"
52 #include "linkgraph/refresh.h"
53 #include "framerate_type.h"
54 #include "autoreplace_cmd.h"
55 #include "misc_cmd.h"
56 #include "train_cmd.h"
57 #include "vehicle_cmd.h"
58 #include "newgrf_roadstop.h"
59 #include "timer/timer.h"
60 #include "timer/timer_game_calendar.h"
61 #include "timer/timer_game_economy.h"
62 #include "timer/timer_game_tick.h"
64 #include "table/strings.h"
66 #include "safeguards.h"
68 /* Number of bits in the hash to use from each vehicle coord */
69 static const uint GEN_HASHX_BITS = 6;
70 static const uint GEN_HASHY_BITS = 6;
72 /* Size of each hash bucket */
73 static const uint GEN_HASHX_BUCKET_BITS = 7;
74 static const uint GEN_HASHY_BUCKET_BITS = 6;
76 /* Compute hash for vehicle coord */
77 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_BASE_SHIFT, GEN_HASHX_BITS)
78 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_BASE_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
79 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
81 /* Maximum size until hash repeats */
82 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_BASE_SHIFT);
83 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_BASE_SHIFT);
85 /* Increments to reach next bucket in hash table */
86 static const int GEN_HASHX_INC = 1;
87 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
89 /* Mask to wrap-around buckets */
90 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
91 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
94 /** The pool with all our precious vehicles. */
95 VehiclePool _vehicle_pool("Vehicle");
96 INSTANTIATE_POOL_METHODS(Vehicle)
99 /**
100 * Determine shared bounds of all sprites.
101 * @param[out] bounds Shared bounds.
103 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
105 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
106 for (uint i = 0; i < this->count; ++i) {
107 const Sprite *spr = GetSprite(this->seq[i].sprite, SpriteType::Normal);
108 if (i == 0) {
109 bounds->left = spr->x_offs;
110 bounds->top = spr->y_offs;
111 bounds->right = spr->width + spr->x_offs - 1;
112 bounds->bottom = spr->height + spr->y_offs - 1;
113 } else {
114 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
115 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
116 int right = spr->width + spr->x_offs - 1;
117 int bottom = spr->height + spr->y_offs - 1;
118 if (right > bounds->right) bounds->right = right;
119 if (bottom > bounds->bottom) bounds->bottom = bottom;
125 * Draw the sprite sequence.
126 * @param x X position
127 * @param y Y position
128 * @param default_pal Vehicle palette
129 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
131 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
133 for (uint i = 0; i < this->count; ++i) {
134 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
135 DrawSprite(this->seq[i].sprite, pal, x, y);
140 * Function to tell if a vehicle needs to be autorenewed
141 * @param *c The vehicle owner
142 * @param use_renew_setting Should the company renew setting be considered?
143 * @return true if the vehicle is old enough for replacement
145 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
147 /* We can always generate the Company pointer when we have the vehicle.
148 * However this takes time and since the Company pointer is often present
149 * when this function is called then it's faster to pass the pointer as an
150 * argument rather than finding it again. */
151 assert(c == Company::Get(this->owner));
153 if (use_renew_setting && !c->settings.engine_renew) return false;
154 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
156 /* Only engines need renewing */
157 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
159 return true;
163 * Service a vehicle and all subsequent vehicles in the consist
165 * @param *v The vehicle or vehicle chain being serviced
167 void VehicleServiceInDepot(Vehicle *v)
169 assert(v != nullptr);
170 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
172 do {
173 v->date_of_last_service = TimerGameEconomy::date;
174 v->date_of_last_service_newgrf = TimerGameCalendar::date;
175 v->breakdowns_since_last_service = 0;
176 v->reliability = v->GetEngine()->reliability;
177 /* Prevent vehicles from breaking down directly after exiting the depot. */
178 v->breakdown_chance /= 4;
179 if (_settings_game.difficulty.vehicle_breakdowns == 1) v->breakdown_chance = 0; // on reduced breakdown
180 v = v->Next();
181 } while (v != nullptr && v->HasEngineType());
185 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
187 * @see NeedsAutomaticServicing()
188 * @return true if the vehicle should go to a depot if a opportunity presents itself.
190 bool Vehicle::NeedsServicing() const
192 /* Stopped or crashed vehicles will not move, as such making unmovable
193 * vehicles to go for service is lame. */
194 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
196 /* Are we ready for the next service cycle? */
197 const Company *c = Company::Get(this->owner);
199 /* Service intervals can be measured in different units, which we handle individually. */
200 if (this->ServiceIntervalIsPercent()) {
201 /* Service interval is in percents. */
202 if (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) return false;
203 } else if (TimerGameEconomy::UsingWallclockUnits()) {
204 /* Service interval is in minutes. */
205 if (this->date_of_last_service + (this->GetServiceInterval() * EconomyTime::DAYS_IN_ECONOMY_MONTH) >= TimerGameEconomy::date) return false;
206 } else {
207 /* Service interval is in days. */
208 if (this->date_of_last_service + this->GetServiceInterval() >= TimerGameEconomy::date) return false;
211 /* If we're servicing anyway, because we have not disabled servicing when
212 * there are no breakdowns or we are playing with breakdowns, bail out. */
213 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
214 _settings_game.difficulty.vehicle_breakdowns != 0) {
215 return true;
218 /* Test whether there is some pending autoreplace.
219 * Note: We do this after the service-interval test.
220 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
221 bool pending_replace = false;
222 Money needed_money = c->settings.engine_renew_money;
223 if (needed_money > GetAvailableMoney(c->index)) return false;
225 for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) {
226 bool replace_when_old = false;
227 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
229 /* Check engine availability */
230 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
231 /* Is the vehicle old if we are not always replacing? */
232 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
234 /* Check refittability */
235 CargoTypes available_cargo_types, union_mask;
236 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
237 /* Is there anything to refit? */
238 if (union_mask != 0) {
239 CargoID cargo_type;
240 CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
241 if (!HasAtMostOneBit(cargo_mask)) {
242 CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(new_engine);
243 if ((cargo_mask & new_engine_default_cargoes) != cargo_mask) {
244 /* We cannot refit to mixed cargoes in an automated way */
245 continue;
247 /* engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required */
248 } else {
249 /* Did the old vehicle carry anything? */
250 if (IsValidCargoID(cargo_type)) {
251 /* We can't refit the vehicle to carry the cargo we want */
252 if (!HasBit(available_cargo_types, cargo_type)) continue;
257 /* Check money.
258 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
259 pending_replace = true;
260 needed_money += 2 * Engine::Get(new_engine)->GetCost();
261 if (needed_money > GetAvailableMoney(c->index)) return false;
264 return pending_replace;
268 * Checks if the current order should be interrupted for a service-in-depot order.
269 * @see NeedsServicing()
270 * @return true if the current order should be interrupted.
272 bool Vehicle::NeedsAutomaticServicing() const
274 if (this->HasDepotOrder()) return false;
275 if (this->current_order.IsType(OT_LOADING)) return false;
276 if (this->current_order.IsType(OT_GOTO_DEPOT) && (this->current_order.GetDepotOrderType() & ODTFB_SERVICE) == 0) return false;
277 return NeedsServicing();
280 uint Vehicle::Crash(bool)
282 assert((this->vehstatus & VS_CRASHED) == 0);
283 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
285 uint pass = 0;
286 /* Stop the vehicle. */
287 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
288 /* crash all wagons, and count passengers */
289 for (Vehicle *v = this; v != nullptr; v = v->Next()) {
290 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
291 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
292 v->vehstatus |= VS_CRASHED;
293 v->MarkAllViewportsDirty();
296 /* Dirty some windows */
297 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
298 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
299 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
300 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
302 delete this->cargo_payment;
303 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
305 return RandomRange(pass + 1); // Randomise deceased passengers.
310 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
311 * @param engine The engine that caused the problem
312 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
313 * @param part2 Part 2 of the error message, taking the engine as parameter 2
314 * @param bug_type Flag to check and set in grfconfig
315 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
317 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
319 const Engine *e = Engine::Get(engine);
320 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
322 /* Missing GRF. Nothing useful can be done in this situation. */
323 if (grfconfig == nullptr) return;
325 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
326 SetBit(grfconfig->grf_bugs, bug_type);
327 SetDParamStr(0, grfconfig->GetName());
328 SetDParam(1, engine);
329 ShowErrorMessage(part1, part2, WL_CRITICAL);
330 if (!_networking) Command<CMD_PAUSE>::Do(DC_EXEC, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, true);
333 /* debug output */
334 SetDParamStr(0, grfconfig->GetName());
335 Debug(grf, 0, "{}", StrMakeValid(GetString(part1)));
337 SetDParam(1, engine);
338 Debug(grf, 0, "{}", StrMakeValid(GetString(part2)));
342 * Logs a bug in GRF and shows a warning message if this
343 * is for the first time this happened.
344 * @param u first vehicle of chain
346 void VehicleLengthChanged(const Vehicle *u)
348 /* show a warning once for each engine in whole game and once for each GRF after each game load */
349 const Engine *engine = u->GetEngine();
350 uint32_t grfid = engine->grf_prop.grffile->grfid;
351 GRFConfig *grfconfig = GetGRFConfig(grfid);
352 if (_gamelog.GRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
353 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
358 * Vehicle constructor.
359 * @param type Type of the new vehicle.
361 Vehicle::Vehicle(VehicleType type)
363 this->type = type;
364 this->coord.left = INVALID_COORD;
365 this->sprite_cache.old_coord.left = INVALID_COORD;
366 this->group_id = DEFAULT_GROUP;
367 this->fill_percent_te_id = INVALID_TE_ID;
368 this->first = this;
369 this->colourmap = PAL_NONE;
370 this->cargo_age_counter = 1;
371 this->last_station_visited = INVALID_STATION;
372 this->last_loading_station = INVALID_STATION;
375 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
376 * lookup times at the expense of memory usage. */
377 const int HASH_BITS = 7;
378 const int HASH_SIZE = 1 << HASH_BITS;
379 const int HASH_MASK = HASH_SIZE - 1;
380 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
381 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
383 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
384 * Profiling results show that 0 is fastest. */
385 const int HASH_RES = 0;
387 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
389 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
391 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
392 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
393 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
394 for (; v != nullptr; v = v->hash_tile_next) {
395 Vehicle *a = proc(v, data);
396 if (find_first && a != nullptr) return a;
398 if (x == xu) break;
400 if (y == yu) break;
403 return nullptr;
408 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
409 * @note Do not call this function directly!
410 * @param x The X location on the map
411 * @param y The Y location on the map
412 * @param data Arbitrary data passed to proc
413 * @param proc The proc that determines whether a vehicle will be "found".
414 * @param find_first Whether to return on the first found or iterate over
415 * all vehicles
416 * @return the best matching or first vehicle (depending on find_first).
418 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
420 const int COLL_DIST = 6;
422 /* Hash area to scan is from xl,yl to xu,yu */
423 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
424 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
425 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
426 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
428 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
432 * Find a vehicle from a specific location. It will call proc for ALL vehicles
433 * on the tile and YOU must make SURE that the "best one" is stored in the
434 * data value and is ALWAYS the same regardless of the order of the vehicles
435 * where proc was called on!
436 * When you fail to do this properly you create an almost untraceable DESYNC!
437 * @note The return value of proc will be ignored.
438 * @note Use this when you have the intention that all vehicles
439 * should be iterated over.
440 * @param x The X location on the map
441 * @param y The Y location on the map
442 * @param data Arbitrary data passed to proc
443 * @param proc The proc that determines whether a vehicle will be "found".
445 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
447 VehicleFromPosXY(x, y, data, proc, false);
451 * Checks whether a vehicle in on a specific location. It will call proc for
452 * vehicles until it returns non-nullptr.
453 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
454 * should be iterated over.
455 * @param x The X location on the map
456 * @param y The Y location on the map
457 * @param data Arbitrary data passed to proc
458 * @param proc The proc that determines whether a vehicle will be "found".
459 * @return True if proc returned non-nullptr.
461 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
463 return VehicleFromPosXY(x, y, data, proc, true) != nullptr;
467 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
468 * @note Do not call this function directly!
469 * @param tile The location on the map
470 * @param data Arbitrary data passed to \a proc.
471 * @param proc The proc that determines whether a vehicle will be "found".
472 * @param find_first Whether to return on the first found or iterate over
473 * all vehicles
474 * @return the best matching or first vehicle (depending on find_first).
476 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
478 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
479 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
481 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
482 for (; v != nullptr; v = v->hash_tile_next) {
483 if (v->tile != tile) continue;
485 Vehicle *a = proc(v, data);
486 if (find_first && a != nullptr) return a;
489 return nullptr;
493 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
494 * on the tile and YOU must make SURE that the "best one" is stored in the
495 * data value and is ALWAYS the same regardless of the order of the vehicles
496 * where proc was called on!
497 * When you fail to do this properly you create an almost untraceable DESYNC!
498 * @note The return value of \a proc will be ignored.
499 * @note Use this function when you have the intention that all vehicles
500 * should be iterated over.
501 * @param tile The location on the map
502 * @param data Arbitrary data passed to \a proc.
503 * @param proc The proc that determines whether a vehicle will be "found".
505 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
507 VehicleFromPos(tile, data, proc, false);
511 * Checks whether a vehicle is on a specific location. It will call \a proc for
512 * vehicles until it returns non-nullptr.
513 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
514 * should be iterated over.
515 * @param tile The location on the map
516 * @param data Arbitrary data passed to \a proc.
517 * @param proc The \a proc that determines whether a vehicle will be "found".
518 * @return True if proc returned non-nullptr.
520 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
522 return VehicleFromPos(tile, data, proc, true) != nullptr;
526 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
527 * @param v Vehicle to examine.
528 * @param data Pointer to height data.
529 * @return \a v if conditions are met, else \c nullptr.
531 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
533 int z = *(int*)data;
535 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return nullptr;
536 if (v->z_pos > z) return nullptr;
538 return v;
542 * Ensure there is no vehicle at the ground at the given position.
543 * @param tile Position to examine.
544 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
546 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
548 int z = GetTileMaxPixelZ(tile);
550 /* Value v is not safe in MP games, however, it is used to generate a local
551 * error message only (which may be different for different machines).
552 * Such a message does not affect MP synchronisation.
554 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
555 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
556 return CommandCost();
559 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
560 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
562 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return nullptr;
563 if (v == (const Vehicle *)data) return nullptr;
565 return v;
569 * Finds vehicle in tunnel / bridge
570 * @param tile first end
571 * @param endtile second end
572 * @param ignore Ignore this vehicle when searching
573 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
575 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
577 /* Value v is not safe in MP games, however, it is used to generate a local
578 * error message only (which may be different for different machines).
579 * Such a message does not affect MP synchronisation.
581 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
582 if (v == nullptr) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
584 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
585 return CommandCost();
588 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
590 TrackBits rail_bits = *(TrackBits *)data;
592 if (v->type != VEH_TRAIN) return nullptr;
594 Train *t = Train::From(v);
595 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return nullptr;
597 return v;
601 * Tests if a vehicle interacts with the specified track bits.
602 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
604 * @param tile The tile.
605 * @param track_bits The track bits.
606 * @return \c true if no train that interacts, is found. \c false if a train is found.
608 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
610 /* Value v is not safe in MP games, however, it is used to generate a local
611 * error message only (which may be different for different machines).
612 * Such a message does not affect MP synchronisation.
614 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
615 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
616 return CommandCost();
619 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
621 Vehicle **old_hash = v->hash_tile_current;
622 Vehicle **new_hash;
624 if (remove) {
625 new_hash = nullptr;
626 } else {
627 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
628 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
629 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
632 if (old_hash == new_hash) return;
634 /* Remove from the old position in the hash table */
635 if (old_hash != nullptr) {
636 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
637 *v->hash_tile_prev = v->hash_tile_next;
640 /* Insert vehicle at beginning of the new position in the hash table */
641 if (new_hash != nullptr) {
642 v->hash_tile_next = *new_hash;
643 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
644 v->hash_tile_prev = new_hash;
645 *new_hash = v;
648 /* Remember current hash position */
649 v->hash_tile_current = new_hash;
652 static Vehicle *_vehicle_viewport_hash[1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)];
654 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y, int old_x, int old_y)
656 Vehicle **old_hash, **new_hash;
658 new_hash = (x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(x, y)];
659 old_hash = (old_x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
661 if (old_hash == new_hash) return;
663 /* remove from hash table? */
664 if (old_hash != nullptr) {
665 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
666 *v->hash_viewport_prev = v->hash_viewport_next;
669 /* insert into hash table? */
670 if (new_hash != nullptr) {
671 v->hash_viewport_next = *new_hash;
672 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
673 v->hash_viewport_prev = new_hash;
674 *new_hash = v;
678 void ResetVehicleHash()
680 for (Vehicle *v : Vehicle::Iterate()) { v->hash_tile_current = nullptr; }
681 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
682 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
685 void ResetVehicleColourMap()
687 for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
691 * List of vehicles that should check for autoreplace this tick.
692 * Mapping of vehicle -> leave depot immediately after autoreplace.
694 using AutoreplaceMap = std::map<VehicleID, bool>;
695 static AutoreplaceMap _vehicles_to_autoreplace;
697 void InitializeVehicles()
699 _vehicles_to_autoreplace.clear();
700 ResetVehicleHash();
703 uint CountVehiclesInChain(const Vehicle *v)
705 uint count = 0;
706 do count++; while ((v = v->Next()) != nullptr);
707 return count;
711 * Check if a vehicle is counted in num_engines in each company struct
712 * @return true if the vehicle is counted in num_engines
714 bool Vehicle::IsEngineCountable() const
716 switch (this->type) {
717 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
718 case VEH_TRAIN:
719 return !this->IsArticulatedPart() && // tenders and other articulated parts
720 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
721 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
722 case VEH_SHIP: return true;
723 default: return false; // Only count company buildable vehicles
728 * Check whether Vehicle::engine_type has any meaning.
729 * @return true if the vehicle has a usable engine type.
731 bool Vehicle::HasEngineType() const
733 switch (this->type) {
734 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
735 case VEH_TRAIN:
736 case VEH_ROAD:
737 case VEH_SHIP: return true;
738 default: return false;
743 * Retrieves the engine of the vehicle.
744 * @return Engine of the vehicle.
745 * @pre HasEngineType() == true
747 const Engine *Vehicle::GetEngine() const
749 return Engine::Get(this->engine_type);
753 * Retrieve the NewGRF the vehicle is tied to.
754 * This is the GRF providing the Action 3 for the engine type.
755 * @return NewGRF associated to the vehicle.
757 const GRFFile *Vehicle::GetGRF() const
759 return this->GetEngine()->GetGRF();
763 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
764 * This is the GRF providing the Action 3 for the engine type.
765 * @return GRF ID of the associated NewGRF.
767 uint32_t Vehicle::GetGRFID() const
769 return this->GetEngine()->GetGRFID();
773 * Shift all dates by given interval.
774 * This is useful if the date has been modified with the cheat menu.
775 * @param interval Number of days to be added or substracted.
777 void Vehicle::ShiftDates(TimerGameEconomy::Date interval)
779 this->date_of_last_service = std::max(this->date_of_last_service + interval, TimerGameEconomy::Date(0));
780 /* date_of_last_service_newgrf is not updated here as it must stay stable
781 * for vehicles outside of a depot. */
785 * Handle the pathfinding result, especially the lost status.
786 * If the vehicle is now lost and wasn't previously fire an
787 * event to the AIs and a news message to the user. If the
788 * vehicle is not lost anymore remove the news message.
789 * @param path_found Whether the vehicle has a path to its destination.
791 void Vehicle::HandlePathfindingResult(bool path_found)
793 if (path_found) {
794 /* Route found, is the vehicle marked with "lost" flag? */
795 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
797 /* Clear the flag as the PF's problem was solved. */
798 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
799 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
800 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
801 /* Delete the news item. */
802 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
803 return;
806 /* Were we already lost? */
807 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
809 /* It is first time the problem occurred, set the "lost" flag. */
810 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
811 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
812 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
814 /* Unbunching data is no longer valid. */
815 this->ResetDepotUnbunching();
817 /* Notify user about the event. */
818 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
819 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
820 SetDParam(0, this->index);
821 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
825 /** Destroy all stuff that (still) needs the virtual functions to work properly */
826 void Vehicle::PreDestructor()
828 if (CleaningPool()) return;
830 if (Station::IsValidID(this->last_station_visited)) {
831 Station *st = Station::Get(this->last_station_visited);
832 st->loading_vehicles.remove(this);
834 HideFillingPercent(&this->fill_percent_te_id);
835 this->CancelReservation(INVALID_STATION, st);
836 delete this->cargo_payment;
837 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
840 if (this->IsEngineCountable()) {
841 GroupStatistics::CountEngine(this, -1);
842 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
843 GroupStatistics::UpdateAutoreplace(this->owner);
845 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
846 DeleteGroupHighlightOfVehicle(this);
849 Company::Get(this->owner)->freeunits[this->type].ReleaseID(this->unitnumber);
851 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
852 Aircraft *a = Aircraft::From(this);
853 Station *st = GetTargetAirportIfValid(a);
854 if (st != nullptr) {
855 const AirportFTA *layout = st->airport.GetFTA()->layout;
856 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
861 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
862 RoadVehicle *v = RoadVehicle::From(this);
863 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
864 /* Leave the drive through roadstop, when you have not already left it. */
865 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
868 if (v->disaster_vehicle != INVALID_VEHICLE) ReleaseDisasterVehicle(v->disaster_vehicle);
871 if (this->Previous() == nullptr) {
872 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
875 if (this->IsPrimaryVehicle()) {
876 CloseWindowById(WC_VEHICLE_VIEW, this->index);
877 CloseWindowById(WC_VEHICLE_ORDERS, this->index);
878 CloseWindowById(WC_VEHICLE_REFIT, this->index);
879 CloseWindowById(WC_VEHICLE_DETAILS, this->index);
880 CloseWindowById(WC_VEHICLE_TIMETABLE, this->index);
881 SetWindowDirty(WC_COMPANY, this->owner);
882 OrderBackup::ClearVehicle(this);
884 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
886 this->cargo.Truncate();
887 DeleteVehicleOrders(this);
888 DeleteDepotHighlightOfVehicle(this);
890 StopGlobalFollowVehicle(this);
893 Vehicle::~Vehicle()
895 if (CleaningPool()) {
896 this->cargo.OnCleanPool();
897 return;
900 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
901 * it may happen that vehicle chain is deleted when visible */
902 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
904 Vehicle *v = this->Next();
905 this->SetNext(nullptr);
907 delete v;
909 UpdateVehicleTileHash(this, true);
910 UpdateVehicleViewportHash(this, INVALID_COORD, 0, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
911 if (this->type != VEH_EFFECT) {
912 DeleteVehicleNews(this->index, INVALID_STRING_ID);
913 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
918 * Adds a vehicle to the list of vehicles that visited a depot this tick
919 * @param *v vehicle to add
921 void VehicleEnteredDepotThisTick(Vehicle *v)
923 /* Vehicle should stop in the depot if it was in 'stopping' state */
924 _vehicles_to_autoreplace[v->index] = !(v->vehstatus & VS_STOPPED);
926 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
927 * stopping in the depot, so we stop it to ensure that it will not reserve
928 * the path out of the depot before we might autoreplace it to a different
929 * engine. The new engine would not own the reserved path we store that we
930 * stopped the vehicle, so autoreplace can start it again */
931 v->vehstatus |= VS_STOPPED;
935 * Age all vehicles, spreading out the action using the current TimerGameCalendar::date_fract.
937 void RunVehicleCalendarDayProc()
939 if (_game_mode != GM_NORMAL) return;
941 /* Run the calendar day proc for every DAY_TICKS vehicle starting at TimerGameCalendar::date_fract. */
942 for (size_t i = TimerGameCalendar::date_fract; i < Vehicle::GetPoolSize(); i += Ticks::DAY_TICKS) {
943 Vehicle *v = Vehicle::Get(i);
944 if (v == nullptr) continue;
945 v->OnNewCalendarDay();
950 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
951 * Each tick, it processes vehicles with "index % DAY_TICKS == TimerGameEconomy::date_fract",
952 * so each day, all vehicles are processes in DAY_TICKS steps.
954 static void RunEconomyVehicleDayProc()
956 if (_game_mode != GM_NORMAL) return;
958 /* Run the economy day proc for every DAY_TICKS vehicle starting at TimerGameEconomy::date_fract. */
959 for (size_t i = TimerGameEconomy::date_fract; i < Vehicle::GetPoolSize(); i += Ticks::DAY_TICKS) {
960 Vehicle *v = Vehicle::Get(i);
961 if (v == nullptr) continue;
963 /* Call the 32-day callback if needed */
964 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
965 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
966 if (callback != CALLBACK_FAILED) {
967 if (HasBit(callback, 0)) {
968 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
971 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
972 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
973 if (callback != 0) v->First()->MarkDirty();
975 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
979 /* This is called once per day for each vehicle, but not in the first tick of the day */
980 v->OnNewEconomyDay();
984 void CallVehicleTicks()
986 _vehicles_to_autoreplace.clear();
988 RunEconomyVehicleDayProc();
991 PerformanceMeasurer framerate(PFE_GL_ECONOMY);
992 for (Station *st : Station::Iterate()) LoadUnloadStation(st);
994 PerformanceAccumulator::Reset(PFE_GL_TRAINS);
995 PerformanceAccumulator::Reset(PFE_GL_ROADVEHS);
996 PerformanceAccumulator::Reset(PFE_GL_SHIPS);
997 PerformanceAccumulator::Reset(PFE_GL_AIRCRAFT);
999 for (Vehicle *v : Vehicle::Iterate()) {
1000 [[maybe_unused]] size_t vehicle_index = v->index;
1002 /* Vehicle could be deleted in this tick */
1003 if (!v->Tick()) {
1004 assert(Vehicle::Get(vehicle_index) == nullptr);
1005 continue;
1008 assert(Vehicle::Get(vehicle_index) == v);
1010 switch (v->type) {
1011 default: break;
1013 case VEH_TRAIN:
1014 case VEH_ROAD:
1015 case VEH_AIRCRAFT:
1016 case VEH_SHIP: {
1017 Vehicle *front = v->First();
1019 if (v->vcache.cached_cargo_age_period != 0) {
1020 v->cargo_age_counter = std::min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
1021 if (--v->cargo_age_counter == 0) {
1022 v->cargo.AgeCargo();
1023 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
1027 /* Do not play any sound when crashed */
1028 if (front->vehstatus & VS_CRASHED) continue;
1030 /* Do not play any sound when in depot or tunnel */
1031 if (v->vehstatus & VS_HIDDEN) continue;
1033 /* Do not play any sound when stopped */
1034 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1036 /* Update motion counter for animation purposes. */
1037 v->motion_counter += front->cur_speed;
1039 /* Check vehicle type specifics */
1040 switch (v->type) {
1041 case VEH_TRAIN:
1042 if (!Train::From(v)->IsEngine()) continue;
1043 break;
1045 case VEH_ROAD:
1046 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1047 break;
1049 case VEH_AIRCRAFT:
1050 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1051 break;
1053 default:
1054 break;
1057 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1058 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1060 /* Play an alternating running sound every 16 ticks */
1061 if (GB(v->tick_counter, 0, 4) == 0) {
1062 /* Play running sound when speed > 0 and not braking */
1063 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1064 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1067 break;
1072 Backup<CompanyID> cur_company(_current_company);
1073 for (auto &it : _vehicles_to_autoreplace) {
1074 Vehicle *v = Vehicle::Get(it.first);
1075 /* Autoreplace needs the current company set as the vehicle owner */
1076 cur_company.Change(v->owner);
1078 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1079 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1080 * they are already leaving the depot again before being replaced. */
1081 if (it.second) v->vehstatus &= ~VS_STOPPED;
1083 /* Store the position of the effect as the vehicle pointer will become invalid later */
1084 int x = v->x_pos;
1085 int y = v->y_pos;
1086 int z = v->z_pos;
1088 const Company *c = Company::Get(_current_company);
1089 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1090 CommandCost res = Command<CMD_AUTOREPLACE_VEHICLE>::Do(DC_EXEC, v->index);
1091 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1093 if (!IsLocalCompany()) continue;
1095 if (res.Succeeded()) {
1096 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1097 continue;
1100 StringID error_message = res.GetErrorMessage();
1101 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1103 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1105 StringID message;
1106 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1107 message = error_message;
1108 } else {
1109 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1112 SetDParam(0, v->index);
1113 SetDParam(1, error_message);
1114 AddVehicleAdviceNewsItem(message, v->index);
1117 cur_company.Restore();
1121 * Add vehicle sprite for drawing to the screen.
1122 * @param v Vehicle to draw.
1124 static void DoDrawVehicle(const Vehicle *v)
1126 PaletteID pal = PAL_NONE;
1128 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1130 /* Check whether the vehicle shall be transparent due to the game state */
1131 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1133 if (v->type == VEH_EFFECT) {
1134 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1135 * However, transparent smoke and bubbles look weird, so always hide them. */
1136 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1137 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1140 StartSpriteCombine();
1141 for (uint i = 0; i < v->sprite_cache.sprite_seq.count; ++i) {
1142 PaletteID pal2 = v->sprite_cache.sprite_seq.seq[i].pal;
1143 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1144 AddSortableSpriteToDraw(v->sprite_cache.sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1145 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1147 EndSpriteCombine();
1151 * Add the vehicle sprites that should be drawn at a part of the screen.
1152 * @param dpi Rectangle being drawn.
1154 void ViewportAddVehicles(DrawPixelInfo *dpi)
1156 /* The bounding rectangle */
1157 const int l = dpi->left;
1158 const int r = dpi->left + dpi->width;
1159 const int t = dpi->top;
1160 const int b = dpi->top + dpi->height;
1162 /* Border size of MAX_VEHICLE_PIXEL_xy */
1163 const int xb = MAX_VEHICLE_PIXEL_X * ZOOM_BASE;
1164 const int yb = MAX_VEHICLE_PIXEL_Y * ZOOM_BASE;
1166 /* The hash area to scan */
1167 int xl, xu, yl, yu;
1169 if (dpi->width + xb < GEN_HASHX_SIZE) {
1170 xl = GEN_HASHX(l - xb);
1171 xu = GEN_HASHX(r);
1172 } else {
1173 /* scan whole hash row */
1174 xl = 0;
1175 xu = GEN_HASHX_MASK;
1178 if (dpi->height + yb < GEN_HASHY_SIZE) {
1179 yl = GEN_HASHY(t - yb);
1180 yu = GEN_HASHY(b);
1181 } else {
1182 /* scan whole column */
1183 yl = 0;
1184 yu = GEN_HASHY_MASK;
1187 for (int y = yl;; y = (y + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1188 for (int x = xl;; x = (x + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1189 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1191 while (v != nullptr) {
1193 if (!(v->vehstatus & VS_HIDDEN) &&
1194 l <= v->coord.right + xb &&
1195 t <= v->coord.bottom + yb &&
1196 r >= v->coord.left - xb &&
1197 b >= v->coord.top - yb)
1200 * This vehicle can potentially be drawn as part of this viewport and
1201 * needs to be revalidated, as the sprite may not be correct.
1203 if (v->sprite_cache.revalidate_before_draw) {
1204 VehicleSpriteSeq seq;
1205 v->GetImage(v->direction, EIT_ON_MAP, &seq);
1207 if (seq.IsValid() && v->sprite_cache.sprite_seq != seq) {
1208 v->sprite_cache.sprite_seq = seq;
1210 * A sprite change may also result in a bounding box change,
1211 * so we need to update the bounding box again before we
1212 * check to see if the vehicle should be drawn. Note that
1213 * we can't interfere with the viewport hash at this point,
1214 * so we keep the original hash on the assumption there will
1215 * not be a significant change in the top and left coordinates
1216 * of the vehicle.
1218 v->UpdateBoundingBoxCoordinates(false);
1222 v->sprite_cache.revalidate_before_draw = false;
1225 if (l <= v->coord.right &&
1226 t <= v->coord.bottom &&
1227 r >= v->coord.left &&
1228 b >= v->coord.top) DoDrawVehicle(v);
1231 v = v->hash_viewport_next;
1234 if (x == xu) break;
1237 if (y == yu) break;
1242 * Find the vehicle close to the clicked coordinates.
1243 * @param vp Viewport clicked in.
1244 * @param x X coordinate in the viewport.
1245 * @param y Y coordinate in the viewport.
1246 * @return Closest vehicle, or \c nullptr if none found.
1248 Vehicle *CheckClickOnVehicle(const Viewport *vp, int x, int y)
1250 Vehicle *found = nullptr;
1251 uint dist, best_dist = UINT_MAX;
1253 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return nullptr;
1255 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1256 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1258 /* Border size of MAX_VEHICLE_PIXEL_xy */
1259 const int xb = MAX_VEHICLE_PIXEL_X * ZOOM_BASE;
1260 const int yb = MAX_VEHICLE_PIXEL_Y * ZOOM_BASE;
1262 /* The hash area to scan */
1263 int xl = GEN_HASHX(x - xb);
1264 int xu = GEN_HASHX(x);
1265 int yl = GEN_HASHY(y - yb);
1266 int yu = GEN_HASHY(y);
1268 for (int hy = yl;; hy = (hy + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1269 for (int hx = xl;; hx = (hx + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1270 Vehicle *v = _vehicle_viewport_hash[hx + hy]; // already masked & 0xFFF
1272 while (v != nullptr) {
1273 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1274 x >= v->coord.left && x <= v->coord.right &&
1275 y >= v->coord.top && y <= v->coord.bottom) {
1277 dist = std::max(
1278 abs(((v->coord.left + v->coord.right) >> 1) - x),
1279 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1282 if (dist < best_dist) {
1283 found = v;
1284 best_dist = dist;
1287 v = v->hash_viewport_next;
1289 if (hx == xu) break;
1291 if (hy == yu) break;
1294 return found;
1298 * Decrease the value of a vehicle.
1299 * @param v %Vehicle to devaluate.
1301 void DecreaseVehicleValue(Vehicle *v)
1303 v->value -= v->value >> 8;
1304 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1307 static const uint8_t _breakdown_chance[64] = {
1308 3, 3, 3, 3, 3, 3, 3, 3,
1309 4, 4, 5, 5, 6, 6, 7, 7,
1310 8, 8, 9, 9, 10, 10, 11, 11,
1311 12, 13, 13, 13, 13, 14, 15, 16,
1312 17, 19, 21, 25, 28, 31, 34, 37,
1313 40, 44, 48, 52, 56, 60, 64, 68,
1314 72, 80, 90, 100, 110, 120, 130, 140,
1315 150, 170, 190, 210, 230, 250, 250, 250,
1318 void CheckVehicleBreakdown(Vehicle *v)
1320 int rel, rel_old;
1322 /* decrease reliability */
1323 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
1324 _settings_game.difficulty.vehicle_breakdowns != 0) {
1325 v->reliability = rel = std::max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1326 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1329 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1330 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1331 v->cur_speed < 5 || _game_mode == GM_MENU) {
1332 return;
1335 uint32_t r = Random();
1337 /* increase chance of failure */
1338 int chance = v->breakdown_chance + 1;
1339 if (Chance16I(1, 25, r)) chance += 25;
1340 v->breakdown_chance = ClampTo<uint8_t>(chance);
1342 /* calculate reliability value to use in comparison */
1343 rel = v->reliability;
1344 if (v->type == VEH_SHIP) rel += 0x6666;
1346 /* reduced breakdowns? */
1347 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1349 /* check if to break down */
1350 if (_breakdown_chance[ClampTo<uint16_t>(rel) >> 10] <= v->breakdown_chance) {
1351 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1352 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1353 v->breakdown_chance = 0;
1358 * Handle all of the aspects of a vehicle breakdown
1359 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1360 * @return true iff the vehicle is stopped because of a breakdown
1361 * @note This function always returns false for aircraft, since these never stop for breakdowns
1363 bool Vehicle::HandleBreakdown()
1365 /* Possible states for Vehicle::breakdown_ctr
1366 * 0 - vehicle is running normally
1367 * 1 - vehicle is currently broken down
1368 * 2 - vehicle is going to break down now
1369 * >2 - vehicle is counting down to the actual breakdown event */
1370 switch (this->breakdown_ctr) {
1371 case 0:
1372 return false;
1374 case 2:
1375 this->breakdown_ctr = 1;
1377 if (this->breakdowns_since_last_service != 255) {
1378 this->breakdowns_since_last_service++;
1381 if (this->type == VEH_AIRCRAFT) {
1382 /* Aircraft just need this flag, the rest is handled elsewhere */
1383 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1384 } else {
1385 this->cur_speed = 0;
1387 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1388 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1389 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1390 (train_or_ship ? SND_10_BREAKDOWN_TRAIN_SHIP : SND_0F_BREAKDOWN_ROADVEHICLE) :
1391 (train_or_ship ? SND_3A_BREAKDOWN_TRAIN_SHIP_TOYLAND : SND_35_BREAKDOWN_ROADVEHICLE_TOYLAND), this);
1394 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1395 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1396 if (u != nullptr) u->animation_state = this->breakdown_delay * 2;
1400 this->MarkDirty(); // Update graphics after speed is zeroed
1401 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1402 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1404 [[fallthrough]];
1405 case 1:
1406 /* Aircraft breakdowns end only when arriving at the airport */
1407 if (this->type == VEH_AIRCRAFT) return false;
1409 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1410 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1411 if (--this->breakdown_delay == 0) {
1412 this->breakdown_ctr = 0;
1413 this->MarkDirty();
1414 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1417 return true;
1419 default:
1420 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1421 return false;
1426 * Update economy age of a vehicle.
1427 * @param v Vehicle to update.
1429 void EconomyAgeVehicle(Vehicle *v)
1431 if (v->economy_age < EconomyTime::MAX_DATE) {
1432 v->economy_age++;
1433 if (v->IsPrimaryVehicle() && v->economy_age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedMinAge(v);
1438 * Update age of a vehicle.
1439 * @param v Vehicle to update.
1441 void AgeVehicle(Vehicle *v)
1443 if (v->age < CalendarTime::MAX_DATE) v->age++;
1445 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1447 auto age = v->age - v->max_age;
1448 for (int32_t i = 0; i <= 4; i++) {
1449 if (age == TimerGameCalendar::DateAtStartOfYear(i)) {
1450 v->reliability_spd_dec <<= 1;
1451 break;
1455 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1457 /* Don't warn if warnings are disabled */
1458 if (!_settings_client.gui.old_vehicle_warn) return;
1460 /* Don't warn about vehicles which are non-primary (e.g., part of an articulated vehicle), don't belong to us, are crashed, or are stopped */
1461 if (v->Previous() != nullptr || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0 || (v->vehstatus & VS_STOPPED) != 0) return;
1463 const Company *c = Company::Get(v->owner);
1464 /* Don't warn if a renew is active */
1465 if (c->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1466 /* Don't warn if a replacement is active */
1467 if (EngineHasReplacementForCompany(c, v->engine_type, v->group_id)) return;
1469 StringID str;
1470 if (age == TimerGameCalendar::DateAtStartOfYear(-1)) {
1471 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1472 } else if (age == TimerGameCalendar::DateAtStartOfYear(0)) {
1473 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1474 } else if (age > TimerGameCalendar::DateAtStartOfYear(0) && (age.base() % CalendarTime::DAYS_IN_LEAP_YEAR) == 0) {
1475 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1476 } else {
1477 return;
1480 SetDParam(0, v->index);
1481 AddVehicleAdviceNewsItem(str, v->index);
1485 * Calculates how full a vehicle is.
1486 * @param front The front vehicle of the consist to check.
1487 * @param colour The string to show depending on if we are unloading or loading
1488 * @return A percentage of how full the Vehicle is.
1489 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1490 * if the vehicle is completely empty or full.
1491 * This is useful for both display and conditional orders.
1493 uint8_t CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1495 int count = 0;
1496 int max = 0;
1497 int cars = 0;
1498 int unloading = 0;
1499 bool loading = false;
1501 bool is_loading = front->current_order.IsType(OT_LOADING);
1503 /* The station may be nullptr when the (colour) string does not need to be set. */
1504 const Station *st = Station::GetIfValid(front->last_station_visited);
1505 assert(colour == nullptr || (st != nullptr && is_loading));
1507 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1508 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1510 /* Count up max and used */
1511 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
1512 count += v->cargo.StoredCount();
1513 max += v->cargo_cap;
1514 if (v->cargo_cap != 0 && colour != nullptr) {
1515 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1516 loading |= !order_no_load &&
1517 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1518 !HasBit(front->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(front->vehicle_flags, VF_STOP_LOADING);
1519 cars++;
1523 if (colour != nullptr) {
1524 if (unloading == 0 && loading) {
1525 *colour = STR_PERCENT_UP;
1526 } else if (unloading == 0 && !loading) {
1527 *colour = STR_PERCENT_NONE;
1528 } else if (cars == unloading || !loading) {
1529 *colour = STR_PERCENT_DOWN;
1530 } else {
1531 *colour = STR_PERCENT_UP_DOWN;
1535 /* Train without capacity */
1536 if (max == 0) return 100;
1538 /* Return the percentage */
1539 if (count * 2 < max) {
1540 /* Less than 50%; round up, so that 0% means really empty. */
1541 return CeilDiv(count * 100, max);
1542 } else {
1543 /* More than 50%; round down, so that 100% means really full. */
1544 return (count * 100) / max;
1549 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1550 * @param v Vehicle that entered a depot.
1552 void VehicleEnterDepot(Vehicle *v)
1554 /* Always work with the front of the vehicle */
1555 assert(v == v->First());
1557 switch (v->type) {
1558 case VEH_TRAIN: {
1559 Train *t = Train::From(v);
1560 SetWindowClassesDirty(WC_TRAINS_LIST);
1561 /* Clear path reservation */
1562 SetDepotReservation(t->tile, false);
1563 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1565 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1566 t->wait_counter = 0;
1567 t->force_proceed = TFP_NONE;
1568 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1569 t->ConsistChanged(CCF_ARRANGE);
1570 break;
1573 case VEH_ROAD:
1574 SetWindowClassesDirty(WC_ROADVEH_LIST);
1575 break;
1577 case VEH_SHIP: {
1578 SetWindowClassesDirty(WC_SHIPS_LIST);
1579 Ship *ship = Ship::From(v);
1580 ship->state = TRACK_BIT_DEPOT;
1581 ship->UpdateCache();
1582 ship->UpdateViewport(true, true);
1583 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1584 break;
1587 case VEH_AIRCRAFT:
1588 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1589 HandleAircraftEnterHangar(Aircraft::From(v));
1590 break;
1591 default: NOT_REACHED();
1593 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1595 if (v->type != VEH_TRAIN) {
1596 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1597 * 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 */
1598 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1600 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1602 v->vehstatus |= VS_HIDDEN;
1603 v->cur_speed = 0;
1605 VehicleServiceInDepot(v);
1607 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1608 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1609 v->MarkDirty();
1611 InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
1613 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1614 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1616 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1618 /* Test whether we are heading for this depot. If not, do nothing.
1619 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1620 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1621 real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1622 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1623 /* We are heading for another depot, keep driving. */
1624 return;
1627 if (v->current_order.IsRefit()) {
1628 Backup<CompanyID> cur_company(_current_company, v->owner);
1629 CommandCost cost = std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DC_EXEC, v->index, v->current_order.GetRefitCargo(), 0xFF, false, false, 0));
1630 cur_company.Restore();
1632 if (cost.Failed()) {
1633 _vehicles_to_autoreplace[v->index] = false;
1634 if (v->owner == _local_company) {
1635 /* Notify the user that we stopped the vehicle */
1636 SetDParam(0, v->index);
1637 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1639 } else if (cost.GetCost() != 0) {
1640 v->profit_this_year -= cost.GetCost() << 8;
1641 if (v->owner == _local_company) {
1642 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1647 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1648 /* Part of orders */
1649 v->DeleteUnreachedImplicitOrders();
1650 UpdateVehicleTimetable(v, true);
1651 v->IncrementImplicitOrderIndex();
1653 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1654 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1655 _vehicles_to_autoreplace[v->index] = false;
1656 /* Invalidate last_loading_station. As the link from the station
1657 * before the stop to the station after the stop can't be predicted
1658 * we shouldn't construct it when the vehicle visits the next stop. */
1659 v->last_loading_station = INVALID_STATION;
1661 /* Clear unbunching data. */
1662 v->ResetDepotUnbunching();
1664 /* Announce that the vehicle is waiting to players and AIs. */
1665 if (v->owner == _local_company) {
1666 SetDParam(0, v->index);
1667 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1669 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1672 /* If we've entered our unbunching depot, record the round trip duration. */
1673 if (v->current_order.GetDepotActionType() & ODATFB_UNBUNCH && v->depot_unbunching_last_departure > 0) {
1674 TimerGameTick::Ticks measured_round_trip = TimerGameTick::counter - v->depot_unbunching_last_departure;
1675 if (v->round_trip_time == 0) {
1676 /* This might be our first round trip. */
1677 v->round_trip_time = measured_round_trip;
1678 } else {
1679 /* If we have a previous trip, smooth the effects of outlier trip calculations caused by jams or other interference. */
1680 v->round_trip_time = Clamp(measured_round_trip, (v->round_trip_time / 2), ClampTo<TimerGameTick::Ticks>(v->round_trip_time * 2));
1684 v->current_order.MakeDummy();
1690 * Update the position of the vehicle. This will update the hash that tells
1691 * which vehicles are on a tile.
1693 void Vehicle::UpdatePosition()
1695 UpdateVehicleTileHash(this, false);
1699 * Update the bounding box co-ordinates of the vehicle
1700 * @param update_cache Update the cached values for previous co-ordinate values
1702 void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache) const
1704 Rect new_coord;
1705 this->sprite_cache.sprite_seq.GetBounds(&new_coord);
1707 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1708 new_coord.left += pt.x;
1709 new_coord.top += pt.y;
1710 new_coord.right += pt.x + 2 * ZOOM_BASE;
1711 new_coord.bottom += pt.y + 2 * ZOOM_BASE;
1713 if (update_cache) {
1715 * If the old coordinates are invalid, set the cache to the new coordinates for correct
1716 * behaviour the next time the coordinate cache is checked.
1718 this->sprite_cache.old_coord = this->coord.left == INVALID_COORD ? new_coord : this->coord;
1720 else {
1721 /* Extend the bounds of the existing cached bounding box so the next dirty window is correct */
1722 this->sprite_cache.old_coord.left = std::min(this->sprite_cache.old_coord.left, this->coord.left);
1723 this->sprite_cache.old_coord.top = std::min(this->sprite_cache.old_coord.top, this->coord.top);
1724 this->sprite_cache.old_coord.right = std::max(this->sprite_cache.old_coord.right, this->coord.right);
1725 this->sprite_cache.old_coord.bottom = std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom);
1728 this->coord = new_coord;
1732 * Update the vehicle on the viewport, updating the right hash and setting the new coordinates.
1733 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1735 void Vehicle::UpdateViewport(bool dirty)
1737 /* If the existing cache is invalid we should ignore it, as it will be set to the current coords by UpdateBoundingBoxCoordinates */
1738 bool ignore_cached_coords = this->sprite_cache.old_coord.left == INVALID_COORD;
1740 this->UpdateBoundingBoxCoordinates(true);
1742 if (ignore_cached_coords) {
1743 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, INVALID_COORD, INVALID_COORD);
1744 } else {
1745 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
1748 if (dirty) {
1749 if (ignore_cached_coords) {
1750 this->sprite_cache.is_viewport_candidate = this->MarkAllViewportsDirty();
1751 } else {
1752 this->sprite_cache.is_viewport_candidate = ::MarkAllViewportsDirty(
1753 std::min(this->sprite_cache.old_coord.left, this->coord.left),
1754 std::min(this->sprite_cache.old_coord.top, this->coord.top),
1755 std::max(this->sprite_cache.old_coord.right, this->coord.right),
1756 std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom));
1762 * Update the position of the vehicle, and update the viewport.
1764 void Vehicle::UpdatePositionAndViewport()
1766 this->UpdatePosition();
1767 this->UpdateViewport(true);
1771 * Marks viewports dirty where the vehicle's image is.
1772 * @return true if at least one viewport has a dirty block
1774 bool Vehicle::MarkAllViewportsDirty() const
1776 return ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1780 * Get position information of a vehicle when moving one pixel in the direction it is facing
1781 * @param v Vehicle to move
1782 * @return Position information after the move
1784 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1786 static const int8_t _delta_coord[16] = {
1787 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1788 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1791 int x = v->x_pos + _delta_coord[v->direction];
1792 int y = v->y_pos + _delta_coord[v->direction + 8];
1794 GetNewVehiclePosResult gp;
1795 gp.x = x;
1796 gp.y = y;
1797 gp.old_tile = v->tile;
1798 gp.new_tile = TileVirtXY(x, y);
1799 return gp;
1802 static const Direction _new_direction_table[] = {
1803 DIR_N, DIR_NW, DIR_W,
1804 DIR_NE, DIR_SE, DIR_SW,
1805 DIR_E, DIR_SE, DIR_S
1808 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1810 int i = 0;
1812 if (y >= v->y_pos) {
1813 if (y != v->y_pos) i += 3;
1814 i += 3;
1817 if (x >= v->x_pos) {
1818 if (x != v->x_pos) i++;
1819 i++;
1822 Direction dir = v->direction;
1824 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1825 if (dirdiff == DIRDIFF_SAME) return dir;
1826 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1830 * Call the tile callback function for a vehicle entering a tile
1831 * @param v Vehicle entering the tile
1832 * @param tile Tile entered
1833 * @param x X position
1834 * @param y Y position
1835 * @return Some meta-data over the to be entered tile.
1836 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1838 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1840 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1844 * Find first unused unit number.
1845 * This does not mark the unit number as used.
1846 * @returns First unused unit number.
1848 UnitID FreeUnitIDGenerator::NextID() const
1850 for (auto it = std::begin(this->used_bitmap); it != std::end(this->used_bitmap); ++it) {
1851 BitmapStorage available = ~(*it);
1852 if (available == 0) continue;
1853 return static_cast<UnitID>(std::distance(std::begin(this->used_bitmap), it) * BITMAP_SIZE + FindFirstBit(available) + 1);
1855 return static_cast<UnitID>(this->used_bitmap.size() * BITMAP_SIZE + 1);
1859 * Use a unit number. If the unit number is not valid it is ignored.
1860 * @param index Unit number to use.
1861 * @returns Unit number used.
1863 UnitID FreeUnitIDGenerator::UseID(UnitID index)
1865 if (index == 0 || index == UINT16_MAX) return index;
1867 index--;
1869 size_t slot = index / BITMAP_SIZE;
1870 if (slot >= this->used_bitmap.size()) this->used_bitmap.resize(slot + 1);
1871 SetBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
1873 return index + 1;
1877 * Release a unit number. If the unit number is not valid it is ignored.
1878 * @param index Unit number to release.
1880 void FreeUnitIDGenerator::ReleaseID(UnitID index)
1882 if (index == 0 || index == UINT16_MAX) return;
1884 index--;
1886 assert(index / BITMAP_SIZE < this->used_bitmap.size());
1887 ClrBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
1891 * Get an unused unit number for a vehicle (if allowed).
1892 * @param type Type of vehicle
1893 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1895 UnitID GetFreeUnitNumber(VehicleType type)
1897 /* Check whether it is allowed to build another vehicle. */
1898 uint max_veh;
1899 switch (type) {
1900 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1901 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1902 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1903 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1904 default: NOT_REACHED();
1907 const Company *c = Company::Get(_current_company);
1908 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1910 return c->freeunits[type].NextID();
1915 * Check whether we can build infrastructure for the given
1916 * vehicle type. This to disable building stations etc. when
1917 * you are not allowed/able to have the vehicle type yet.
1918 * @param type the vehicle type to check this for
1919 * @return true if there is any reason why you may build
1920 * the infrastructure for the given vehicle type
1922 bool CanBuildVehicleInfrastructure(VehicleType type, uint8_t subtype)
1924 assert(IsCompanyBuildableVehicleType(type));
1926 if (!Company::IsValidID(_local_company)) return false;
1928 UnitID max;
1929 switch (type) {
1930 case VEH_TRAIN:
1931 if (!HasAnyRailTypesAvail(_local_company)) return false;
1932 max = _settings_game.vehicle.max_trains;
1933 break;
1934 case VEH_ROAD:
1935 if (!HasAnyRoadTypesAvail(_local_company, (RoadTramType)subtype)) return false;
1936 max = _settings_game.vehicle.max_roadveh;
1937 break;
1938 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1939 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1940 default: NOT_REACHED();
1943 /* We can build vehicle infrastructure when we may build the vehicle type */
1944 if (max > 0) {
1945 /* Can we actually build the vehicle type? */
1946 for (const Engine *e : Engine::IterateType(type)) {
1947 if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue;
1948 if (HasBit(e->company_avail, _local_company)) return true;
1950 return false;
1953 /* We should be able to build infrastructure when we have the actual vehicle type */
1954 for (const Vehicle *v : Vehicle::Iterate()) {
1955 if (v->type == VEH_ROAD && GetRoadTramType(RoadVehicle::From(v)->roadtype) != (RoadTramType)subtype) continue;
1956 if (v->owner == _local_company && v->type == type) return true;
1959 return false;
1964 * Determines the #LiveryScheme for a vehicle.
1965 * @param engine_type Engine of the vehicle.
1966 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1967 * @param v the vehicle, \c nullptr if in purchase list etc.
1968 * @return livery scheme to use.
1970 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1972 CargoID cargo_type = v == nullptr ? INVALID_CARGO : v->cargo_type;
1973 const Engine *e = Engine::Get(engine_type);
1974 switch (e->type) {
1975 default: NOT_REACHED();
1976 case VEH_TRAIN:
1977 if (v != nullptr && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1978 /* Wagonoverrides use the colour scheme of the front engine.
1979 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1980 engine_type = parent_engine_type;
1981 e = Engine::Get(engine_type);
1982 /* Note: Luckily cargo_type is not needed for engines */
1985 if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
1986 if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
1987 assert(IsValidCargoID(cargo_type));
1988 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1989 if (!CargoSpec::Get(cargo_type)->is_freight) {
1990 if (parent_engine_type == INVALID_ENGINE) {
1991 return LS_PASSENGER_WAGON_STEAM;
1992 } else {
1993 bool is_mu = HasBit(EngInfo(parent_engine_type)->misc_flags, EF_RAIL_IS_MU);
1994 switch (RailVehInfo(parent_engine_type)->engclass) {
1995 default: NOT_REACHED();
1996 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1997 case EC_DIESEL: return is_mu ? LS_DMU : LS_PASSENGER_WAGON_DIESEL;
1998 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_PASSENGER_WAGON_ELECTRIC;
1999 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
2000 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
2003 } else {
2004 return LS_FREIGHT_WAGON;
2006 } else {
2007 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
2009 switch (e->u.rail.engclass) {
2010 default: NOT_REACHED();
2011 case EC_STEAM: return LS_STEAM;
2012 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
2013 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
2014 case EC_MONORAIL: return LS_MONORAIL;
2015 case EC_MAGLEV: return LS_MAGLEV;
2019 case VEH_ROAD:
2020 /* Always use the livery of the front */
2021 if (v != nullptr && parent_engine_type != INVALID_ENGINE) {
2022 engine_type = parent_engine_type;
2023 e = Engine::Get(engine_type);
2024 cargo_type = v->First()->cargo_type;
2026 if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
2027 if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
2028 assert(IsValidCargoID(cargo_type));
2030 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
2031 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
2032 /* Tram */
2033 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
2034 } else {
2035 /* Bus or truck */
2036 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
2039 case VEH_SHIP:
2040 if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
2041 if (!IsValidCargoID(cargo_type)) cargo_type = GetCargoIDByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
2042 assert(IsValidCargoID(cargo_type));
2043 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
2045 case VEH_AIRCRAFT:
2046 switch (e->u.air.subtype) {
2047 case AIR_HELI: return LS_HELICOPTER;
2048 case AIR_CTOL: return LS_SMALL_PLANE;
2049 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
2050 default: NOT_REACHED();
2056 * Determines the livery for a vehicle.
2057 * @param engine_type EngineID of the vehicle
2058 * @param company Owner of the vehicle
2059 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
2060 * @param v the vehicle. nullptr if in purchase list etc.
2061 * @param livery_setting The livery settings to use for acquiring the livery information.
2062 * @return livery to use
2064 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, uint8_t livery_setting)
2066 const Company *c = Company::Get(company);
2067 LiveryScheme scheme = LS_DEFAULT;
2069 if (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company)) {
2070 if (v != nullptr) {
2071 const Group *g = Group::GetIfValid(v->First()->group_id);
2072 if (g != nullptr) {
2073 /* Traverse parents until we find a livery or reach the top */
2074 while (g->livery.in_use == 0 && g->parent != INVALID_GROUP) {
2075 g = Group::Get(g->parent);
2077 if (g->livery.in_use != 0) return &g->livery;
2081 /* The default livery is always available for use, but its in_use flag determines
2082 * whether any _other_ liveries are in use. */
2083 if (c->livery[LS_DEFAULT].in_use != 0) {
2084 /* Determine the livery scheme to use */
2085 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
2089 return &c->livery[scheme];
2093 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
2095 PaletteID map = (v != nullptr) ? v->colourmap : PAL_NONE;
2097 /* Return cached value if any */
2098 if (map != PAL_NONE) return map;
2100 const Engine *e = Engine::Get(engine_type);
2102 /* Check if we should use the colour map callback */
2103 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
2104 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
2105 /* Failure means "use the default two-colour" */
2106 if (callback != CALLBACK_FAILED) {
2107 static_assert(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2108 map = GB(callback, 0, 14);
2109 /* If bit 14 is set, then the company colours are applied to the
2110 * map else it's returned as-is. */
2111 if (!HasBit(callback, 14)) {
2112 /* Update cache */
2113 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2114 return map;
2119 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
2121 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
2123 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2124 if (!Company::IsValidID(company)) return map;
2126 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2128 map += livery->colour1;
2129 if (twocc) map += livery->colour2 * 16;
2131 /* Update cache */
2132 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2133 return map;
2137 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2138 * @param engine_type ID of engine
2139 * @param company ID of company
2140 * @return A ready-to-use palette modifier
2142 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2144 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, nullptr);
2148 * Get the colour map for a vehicle.
2149 * @param v Vehicle to get colour map for
2150 * @return A ready-to-use palette modifier
2152 PaletteID GetVehiclePalette(const Vehicle *v)
2154 if (v->IsGroundVehicle()) {
2155 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
2158 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
2162 * Delete all implicit orders which were not reached.
2164 void Vehicle::DeleteUnreachedImplicitOrders()
2166 if (this->IsGroundVehicle()) {
2167 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2168 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2169 /* Do not delete orders, only skip them */
2170 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2171 this->cur_implicit_order_index = this->cur_real_order_index;
2172 InvalidateVehicleOrder(this, 0);
2173 return;
2177 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2178 while (order != nullptr) {
2179 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2181 if (order->IsType(OT_IMPLICIT)) {
2182 DeleteOrder(this, this->cur_implicit_order_index);
2183 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2184 order = this->GetOrder(this->cur_implicit_order_index);
2185 } else {
2186 /* Skip non-implicit orders, e.g. service-orders */
2187 order = order->next;
2188 this->cur_implicit_order_index++;
2191 /* Wrap around */
2192 if (order == nullptr) {
2193 order = this->GetOrder(0);
2194 this->cur_implicit_order_index = 0;
2200 * Prepare everything to begin the loading when arriving at a station.
2201 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2203 void Vehicle::BeginLoading()
2205 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2207 TimerGameTick::Ticks travel_time = TimerGameTick::counter - this->last_loading_tick;
2208 if (this->current_order.IsType(OT_GOTO_STATION) &&
2209 this->current_order.GetDestination() == this->last_station_visited) {
2210 this->DeleteUnreachedImplicitOrders();
2212 /* Now both order indices point to the destination station, and we can start loading */
2213 this->current_order.MakeLoading(true);
2214 UpdateVehicleTimetable(this, true);
2216 /* Furthermore add the Non Stop flag to mark that this station
2217 * is the actual destination of the vehicle, which is (for example)
2218 * necessary to be known for HandleTrainLoading to determine
2219 * whether the train is lost or not; not marking a train lost
2220 * that arrives at random stations is bad. */
2221 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2223 } else {
2224 /* We weren't scheduled to stop here. Insert an implicit order
2225 * to show that we are stopping here.
2226 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2227 * the 'wrong' terminal when skipping orders etc. */
2228 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2229 if (this->IsGroundVehicle() &&
2230 (in_list == nullptr || !in_list->IsType(OT_IMPLICIT) ||
2231 in_list->GetDestination() != this->last_station_visited)) {
2232 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2233 /* Do not create consecutive duplicates of implicit orders */
2234 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2235 if (prev_order == nullptr ||
2236 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2237 prev_order->GetDestination() != this->last_station_visited) {
2239 /* Prefer deleting implicit orders instead of inserting new ones,
2240 * so test whether the right order follows later. In case of only
2241 * implicit orders treat the last order in the list like an
2242 * explicit one, except if the overall number of orders surpasses
2243 * IMPLICIT_ORDER_ONLY_CAP. */
2244 int target_index = this->cur_implicit_order_index;
2245 bool found = false;
2246 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2247 const Order *order = this->GetOrder(target_index);
2248 if (order == nullptr) break; // No orders.
2249 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2250 found = true;
2251 break;
2253 target_index++;
2254 if (target_index >= this->orders->GetNumOrders()) {
2255 if (this->GetNumManualOrders() == 0 &&
2256 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2257 break;
2259 target_index = 0;
2261 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2264 if (found) {
2265 if (suppress_implicit_orders) {
2266 /* Skip to the found order */
2267 this->cur_implicit_order_index = target_index;
2268 InvalidateVehicleOrder(this, 0);
2269 } else {
2270 /* Delete all implicit orders up to the station we just reached */
2271 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2272 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2273 if (order->IsType(OT_IMPLICIT)) {
2274 DeleteOrder(this, this->cur_implicit_order_index);
2275 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2276 order = this->GetOrder(this->cur_implicit_order_index);
2277 } else {
2278 /* Skip non-implicit orders, e.g. service-orders */
2279 order = order->next;
2280 this->cur_implicit_order_index++;
2283 /* Wrap around */
2284 if (order == nullptr) {
2285 order = this->GetOrder(0);
2286 this->cur_implicit_order_index = 0;
2288 assert(order != nullptr);
2291 } else if (!suppress_implicit_orders &&
2292 ((this->orders == nullptr ? OrderList::CanAllocateItem() : this->orders->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2293 Order::CanAllocateItem()) {
2294 /* Insert new implicit order */
2295 Order *implicit_order = new Order();
2296 implicit_order->MakeImplicit(this->last_station_visited);
2297 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2298 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2300 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2301 * Reenable it for this vehicle */
2302 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2303 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2307 this->current_order.MakeLoading(false);
2310 if (this->last_loading_station != INVALID_STATION &&
2311 this->last_loading_station != this->last_station_visited &&
2312 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2313 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2314 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited, travel_time);
2317 PrepareUnload(this);
2319 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2320 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2321 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2322 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2324 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2325 this->cur_speed = 0;
2326 this->MarkDirty();
2330 * Return all reserved cargo packets to the station and reset all packets
2331 * staged for transfer.
2332 * @param st the station where the reserved packets should go.
2334 void Vehicle::CancelReservation(StationID next, Station *st)
2336 for (Vehicle *v = this; v != nullptr; v = v->next) {
2337 VehicleCargoList &cargo = v->cargo;
2338 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2339 Debug(misc, 1, "cancelling cargo reservation");
2340 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next, v->tile);
2342 cargo.KeepAll();
2347 * Perform all actions when leaving a station.
2348 * @pre this->current_order.IsType(OT_LOADING)
2350 void Vehicle::LeaveStation()
2352 assert(this->current_order.IsType(OT_LOADING));
2354 delete this->cargo_payment;
2355 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
2357 /* Only update the timetable if the vehicle was supposed to stop here. */
2358 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2360 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2361 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2362 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2363 /* Refresh next hop stats to make sure we've done that at least once
2364 * during the stop and that refit_cap == cargo_cap for each vehicle in
2365 * the consist. */
2366 this->ResetRefitCaps();
2367 LinkRefresher::Run(this);
2369 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2370 this->last_loading_station = this->last_station_visited;
2371 this->last_loading_tick = TimerGameTick::counter;
2372 } else {
2373 /* if the vehicle couldn't load and had to unload or transfer everything
2374 * set the last loading station to invalid as it will leave empty. */
2375 this->last_loading_station = INVALID_STATION;
2379 this->current_order.MakeLeaveStation();
2380 Station *st = Station::Get(this->last_station_visited);
2381 this->CancelReservation(INVALID_STATION, st);
2382 st->loading_vehicles.remove(this);
2384 HideFillingPercent(&this->fill_percent_te_id);
2385 trip_occupancy = CalcPercentVehicleFilled(this, nullptr);
2387 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2388 /* Trigger station animation (trains only) */
2389 if (IsTileType(this->tile, MP_STATION)) {
2390 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2391 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2394 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2396 if (this->type == VEH_ROAD && !(this->vehstatus & VS_CRASHED)) {
2397 /* Trigger road stop animation */
2398 if (IsStationRoadStopTile(this->tile)) {
2399 TriggerRoadStopRandomisation(st, this->tile, RSRT_VEH_DEPARTS);
2400 TriggerRoadStopAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2405 this->MarkDirty();
2409 * Reset all refit_cap in the consist to cargo_cap.
2411 void Vehicle::ResetRefitCaps()
2413 for (Vehicle *v = this; v != nullptr; v = v->Next()) v->refit_cap = v->cargo_cap;
2417 * Release the vehicle's unit number.
2419 void Vehicle::ReleaseUnitNumber()
2421 Company::Get(this->owner)->freeunits[this->type].ReleaseID(this->unitnumber);
2422 this->unitnumber = 0;
2426 * Handle the loading of the vehicle; when not it skips through dummy
2427 * orders and does nothing in all other cases.
2428 * @param mode is the non-first call for this vehicle in this tick?
2430 void Vehicle::HandleLoading(bool mode)
2432 switch (this->current_order.GetType()) {
2433 case OT_LOADING: {
2434 TimerGameTick::Ticks wait_time = std::max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2436 /* Not the first call for this tick, or still loading */
2437 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2439 this->PlayLeaveStationSound();
2441 this->LeaveStation();
2443 /* Only advance to next order if we just loaded at the current one */
2444 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2445 if (order == nullptr ||
2446 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2447 order->GetDestination() != this->last_station_visited) {
2448 return;
2450 break;
2453 case OT_DUMMY: break;
2455 default: return;
2458 this->IncrementImplicitOrderIndex();
2462 * Check if the current vehicle has a full load order.
2463 * @return true Iff this vehicle has a full load order.
2465 bool Vehicle::HasFullLoadOrder() const
2467 for (Order *o : this->Orders()) {
2468 if (o->IsType(OT_GOTO_STATION) && o->GetLoadType() & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY)) return true;
2470 return false;
2474 * Check if the current vehicle has a conditional order.
2475 * @return true Iff this vehicle has a conditional order.
2477 bool Vehicle::HasConditionalOrder() const
2479 for (Order *o : this->Orders()) {
2480 if (o->IsType(OT_CONDITIONAL)) return true;
2482 return false;
2486 * Check if the current vehicle has an unbunching order.
2487 * @return true Iff this vehicle has an unbunching order.
2489 bool Vehicle::HasUnbunchingOrder() const
2491 for (Order *o : this->Orders()) {
2492 if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotActionType() & ODATFB_UNBUNCH) return true;
2494 return false;
2498 * Check if the previous order is a depot unbunching order.
2499 * @return true Iff the previous order is a depot order with the unbunch flag.
2501 static bool PreviousOrderIsUnbunching(const Vehicle *v)
2503 /* If we are headed for the first order, we must wrap around back to the last order. */
2504 bool is_first_order = (v->GetOrder(v->cur_implicit_order_index) == v->GetFirstOrder());
2505 Order *previous_order = (is_first_order) ? v->GetLastOrder() : v->GetOrder(v->cur_implicit_order_index - 1);
2507 if (previous_order == nullptr || !previous_order->IsType(OT_GOTO_DEPOT)) return false;
2508 return (previous_order->GetDepotActionType() & ODATFB_UNBUNCH) != 0;
2512 * Leave an unbunching depot and calculate the next departure time for shared order vehicles.
2514 void Vehicle::LeaveUnbunchingDepot()
2516 /* Don't do anything if this is not our unbunching order. */
2517 if (!PreviousOrderIsUnbunching(this)) return;
2519 /* Set the start point for this round trip time. */
2520 this->depot_unbunching_last_departure = TimerGameTick::counter;
2522 /* Tell the timetable we are now "on time." */
2523 this->lateness_counter = 0;
2524 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
2526 /* Find the average travel time of vehicles that we share orders with. */
2527 int num_vehicles = 0;
2528 TimerGameTick::Ticks total_travel_time = 0;
2530 Vehicle *u = this->FirstShared();
2531 for (; u != nullptr; u = u->NextShared()) {
2532 /* Ignore vehicles that are manually stopped or crashed. */
2533 if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
2535 num_vehicles++;
2536 total_travel_time += u->round_trip_time;
2539 /* Make sure we cannot divide by 0. */
2540 num_vehicles = std::max(num_vehicles, 1);
2542 /* Calculate the separation by finding the average travel time, then calculating equal separation (minimum 1 tick) between vehicles. */
2543 TimerGameTick::Ticks separation = std::max((total_travel_time / num_vehicles / num_vehicles), 1);
2544 TimerGameTick::TickCounter next_departure = TimerGameTick::counter + separation;
2546 /* Set the departure time of all vehicles that we share orders with. */
2547 u = this->FirstShared();
2548 for (; u != nullptr; u = u->NextShared()) {
2549 /* Ignore vehicles that are manually stopped or crashed. */
2550 if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
2552 u->depot_unbunching_next_departure = next_departure;
2553 SetWindowDirty(WC_VEHICLE_VIEW, u->index);
2558 * Check whether a vehicle inside a depot is waiting for unbunching.
2559 * @return True if the vehicle must continue waiting, or false if it may try to leave the depot.
2561 bool Vehicle::IsWaitingForUnbunching() const
2563 assert(this->IsInDepot());
2565 /* Don't bother if there are no vehicles sharing orders. */
2566 if (!this->IsOrderListShared()) return false;
2568 /* Don't do anything if there aren't enough orders. */
2569 if (this->GetNumOrders() <= 1) return false;
2571 /* Don't do anything if this is not our unbunching order. */
2572 if (!PreviousOrderIsUnbunching(this)) return false;
2574 return (this->depot_unbunching_next_departure > TimerGameTick::counter);
2578 * Send this vehicle to the depot using the given command(s).
2579 * @param flags the command flags (like execute and such).
2580 * @param command the command to execute.
2581 * @return the cost of the depot action.
2583 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2585 CommandCost ret = CheckOwnership(this->owner);
2586 if (ret.Failed()) return ret;
2588 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2589 if (this->IsStoppedInDepot()) return CMD_ERROR;
2591 /* No matter why we're headed to the depot, unbunching data is no longer valid. */
2592 if (flags & DC_EXEC) this->ResetDepotUnbunching();
2594 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2595 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2596 if (HasFlag(command, DepotCommand::Service) == halt_in_depot) {
2597 /* We called with a different DEPOT_SERVICE setting.
2598 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2599 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2600 if (flags & DC_EXEC) {
2601 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2602 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2603 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2605 return CommandCost();
2608 if (HasFlag(command, DepotCommand::DontCancel)) return CMD_ERROR; // Requested no cancellation of depot orders
2609 if (flags & DC_EXEC) {
2610 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2611 * then skip to the next order; effectively cancelling this forced service */
2612 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2614 if (this->IsGroundVehicle()) {
2615 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2616 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2619 this->current_order.MakeDummy();
2620 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2622 return CommandCost();
2625 ClosestDepot closestDepot = this->FindClosestDepot();
2626 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};
2627 if (!closestDepot.found) return_cmd_error(no_depot[this->type]);
2629 if (flags & DC_EXEC) {
2630 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2632 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2633 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2634 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2637 this->SetDestTile(closestDepot.location);
2638 this->current_order.MakeGoToDepot(closestDepot.destination, ODTF_MANUAL);
2639 if (!HasFlag(command, DepotCommand::Service)) this->current_order.SetDepotActionType(ODATFB_HALT);
2640 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2642 /* If there is no depot in front and the train is not already reversing, reverse automatically (trains only) */
2643 if (this->type == VEH_TRAIN && (closestDepot.reverse ^ HasBit(Train::From(this)->flags, VRF_REVERSING))) {
2644 Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DC_EXEC, this->index, false);
2647 if (this->type == VEH_AIRCRAFT) {
2648 Aircraft *a = Aircraft::From(this);
2649 if (a->state == FLYING && a->targetairport != closestDepot.destination) {
2650 /* The aircraft is now heading for a different hangar than the next in the orders */
2651 AircraftNextAirportPos_and_Order(a);
2656 return CommandCost();
2661 * Update the cached visual effect.
2662 * @param allow_power_change true if the wagon-is-powered-state may change.
2664 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2666 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2667 const Engine *e = this->GetEngine();
2669 /* Evaluate properties */
2670 uint8_t visual_effect;
2671 switch (e->type) {
2672 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2673 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2674 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2675 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2678 /* Check powered wagon / visual effect callback */
2679 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2680 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2682 if (callback != CALLBACK_FAILED) {
2683 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2685 callback = GB(callback, 0, 8);
2686 /* Avoid accidentally setting 'visual_effect' to the default value
2687 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2688 if (callback == VE_DEFAULT) {
2689 assert(HasBit(callback, VE_DISABLE_EFFECT));
2690 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2692 visual_effect = callback;
2696 /* Apply default values */
2697 if (visual_effect == VE_DEFAULT ||
2698 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2699 /* Only train engines have default effects.
2700 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2701 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2702 if (visual_effect == VE_DEFAULT) {
2703 visual_effect = 1 << VE_DISABLE_EFFECT;
2704 } else {
2705 SetBit(visual_effect, VE_DISABLE_EFFECT);
2707 } else {
2708 if (visual_effect == VE_DEFAULT) {
2709 /* Also set the offset */
2710 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2712 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2716 this->vcache.cached_vis_effect = visual_effect;
2718 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2719 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2720 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2724 static const int8_t _vehicle_smoke_pos[8] = {
2725 1, 1, 1, 0, -1, -1, -1, 0
2729 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2730 * @param v Vehicle to create effects for.
2732 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2734 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2735 if (callback == CALLBACK_FAILED) return;
2737 uint count = GB(callback, 0, 2);
2738 bool auto_center = HasBit(callback, 13);
2739 bool auto_rotate = !HasBit(callback, 14);
2741 int8_t l_center = 0;
2742 if (auto_center) {
2743 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2744 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2745 } else {
2746 /* For trains: Compute offset from vehicle position to sprite position */
2747 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2750 Direction l_dir = v->direction;
2751 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2752 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2754 int8_t x_center = _vehicle_smoke_pos[l_dir] * l_center;
2755 int8_t y_center = _vehicle_smoke_pos[t_dir] * l_center;
2757 for (uint i = 0; i < count; i++) {
2758 uint32_t reg = GetRegister(0x100 + i);
2759 uint type = GB(reg, 0, 8);
2760 int8_t x = GB(reg, 8, 8);
2761 int8_t y = GB(reg, 16, 8);
2762 int8_t z = GB(reg, 24, 8);
2764 if (auto_rotate) {
2765 int8_t l = x;
2766 int8_t t = y;
2767 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2768 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2771 if (type >= 0xF0) {
2772 switch (type) {
2773 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2774 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2775 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2776 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2777 default: break;
2784 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2785 * @pre this->IsPrimaryVehicle()
2787 void Vehicle::ShowVisualEffect() const
2789 assert(this->IsPrimaryVehicle());
2790 bool sound = false;
2792 /* Do not show any smoke when:
2793 * - vehicle smoke is disabled by the player
2794 * - the vehicle is slowing down or stopped (by the player)
2795 * - the vehicle is moving very slowly
2797 if (_settings_game.vehicle.smoke_amount == 0 ||
2798 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2799 this->cur_speed < 2) {
2800 return;
2803 /* Use the speed as limited by underground and orders. */
2804 uint max_speed = this->GetCurrentMaxSpeed();
2806 if (this->type == VEH_TRAIN) {
2807 const Train *t = Train::From(this);
2808 /* For trains, do not show any smoke when:
2809 * - the train is reversing
2810 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2812 if (HasBit(t->flags, VRF_REVERSING) ||
2813 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2814 t->cur_speed >= max_speed)) {
2815 return;
2819 const Vehicle *v = this;
2821 do {
2822 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2823 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2824 VisualEffectSpawnModel effect_model = VESM_NONE;
2825 if (advanced) {
2826 effect_offset = VE_OFFSET_CENTRE;
2827 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2828 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2829 } else {
2830 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2831 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2832 static_assert((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2833 static_assert((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2834 static_assert((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2837 /* Show no smoke when:
2838 * - Smoke has been disabled for this vehicle
2839 * - The vehicle is not visible
2840 * - The vehicle is under a bridge
2841 * - The vehicle is on a depot tile
2842 * - The vehicle is on a tunnel tile
2843 * - The vehicle is a train engine that is currently unpowered */
2844 if (effect_model == VESM_NONE ||
2845 v->vehstatus & VS_HIDDEN ||
2846 IsBridgeAbove(v->tile) ||
2847 IsDepotTile(v->tile) ||
2848 IsTunnelTile(v->tile) ||
2849 (v->type == VEH_TRAIN &&
2850 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2851 continue;
2854 EffectVehicleType evt = EV_END;
2855 switch (effect_model) {
2856 case VESM_STEAM:
2857 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2858 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2859 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2860 * REGULATION:
2861 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2862 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2863 evt = EV_STEAM_SMOKE;
2865 break;
2867 case VESM_DIESEL: {
2868 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2869 * when smoke emission stops.
2870 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2871 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2872 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2873 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2874 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2875 * maximum speed no diesel_smoke is emitted.
2876 * REGULATION:
2877 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2878 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2879 int power_weight_effect = 0;
2880 if (v->type == VEH_TRAIN) {
2881 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2883 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2884 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2885 evt = EV_DIESEL_SMOKE;
2887 break;
2890 case VESM_ELECTRIC:
2891 /* Electric train's spark - more often occurs when train is departing (more load)
2892 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2893 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2894 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2895 * REGULATION:
2896 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2897 if (GB(v->tick_counter, 0, 2) == 0 &&
2898 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2899 evt = EV_ELECTRIC_SPARK;
2901 break;
2903 default:
2904 NOT_REACHED();
2907 if (evt != EV_END && advanced) {
2908 sound = true;
2909 SpawnAdvancedVisualEffect(v);
2910 } else if (evt != EV_END) {
2911 sound = true;
2913 /* The effect offset is relative to a point 4 units behind the vehicle's
2914 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2915 * correction factor. */
2916 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2918 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2919 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2921 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2922 x = -x;
2923 y = -y;
2926 CreateEffectVehicleRel(v, x, y, 10, evt);
2928 } while ((v = v->Next()) != nullptr);
2930 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2934 * Set the next vehicle of this vehicle.
2935 * @param next the next vehicle. nullptr removes the next vehicle.
2937 void Vehicle::SetNext(Vehicle *next)
2939 assert(this != next);
2941 if (this->next != nullptr) {
2942 /* We had an old next vehicle. Update the first and previous pointers */
2943 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2944 v->first = this->next;
2946 this->next->previous = nullptr;
2949 this->next = next;
2951 if (this->next != nullptr) {
2952 /* A new next vehicle. Update the first and previous pointers */
2953 if (this->next->previous != nullptr) this->next->previous->next = nullptr;
2954 this->next->previous = this;
2955 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2956 v->first = this->first;
2962 * Adds this vehicle to a shared vehicle chain.
2963 * @param shared_chain a vehicle of the chain with shared vehicles.
2964 * @pre !this->IsOrderListShared()
2966 void Vehicle::AddToShared(Vehicle *shared_chain)
2968 assert(this->previous_shared == nullptr && this->next_shared == nullptr);
2970 if (shared_chain->orders == nullptr) {
2971 assert(shared_chain->previous_shared == nullptr);
2972 assert(shared_chain->next_shared == nullptr);
2973 this->orders = shared_chain->orders = new OrderList(nullptr, shared_chain);
2976 this->next_shared = shared_chain->next_shared;
2977 this->previous_shared = shared_chain;
2979 shared_chain->next_shared = this;
2981 if (this->next_shared != nullptr) this->next_shared->previous_shared = this;
2983 shared_chain->orders->AddVehicle(this);
2987 * Removes the vehicle from the shared order list.
2989 void Vehicle::RemoveFromShared()
2991 /* Remember if we were first and the old window number before RemoveVehicle()
2992 * as this changes first if needed. */
2993 bool were_first = (this->FirstShared() == this);
2994 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2996 this->orders->RemoveVehicle(this);
2998 if (!were_first) {
2999 /* We are not the first shared one, so only relink our previous one. */
3000 this->previous_shared->next_shared = this->NextShared();
3003 if (this->next_shared != nullptr) this->next_shared->previous_shared = this->previous_shared;
3006 if (this->orders->GetNumVehicles() == 1) {
3007 /* When there is only one vehicle, remove the shared order list window. */
3008 CloseWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
3009 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
3010 } else if (were_first) {
3011 /* If we were the first one, update to the new first one.
3012 * Note: FirstShared() is already the new first */
3013 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
3016 this->next_shared = nullptr;
3017 this->previous_shared = nullptr;
3020 static IntervalTimer<TimerGameEconomy> _economy_vehicles_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::VEHICLE}, [](auto)
3022 for (Vehicle *v : Vehicle::Iterate()) {
3023 if (v->IsPrimaryVehicle()) {
3024 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
3025 Money profit = v->GetDisplayProfitThisYear();
3026 if (v->economy_age >= VEHICLE_PROFIT_MIN_AGE && profit < 0) {
3027 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
3028 SetDParam(0, v->index);
3029 SetDParam(1, profit);
3030 AddVehicleAdviceNewsItem(
3031 TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD : STR_NEWS_VEHICLE_UNPROFITABLE_YEAR,
3032 v->index);
3034 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
3037 v->profit_last_year = v->profit_this_year;
3038 v->profit_this_year = 0;
3039 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
3042 GroupStatistics::UpdateProfits();
3043 SetWindowClassesDirty(WC_TRAINS_LIST);
3044 SetWindowClassesDirty(WC_SHIPS_LIST);
3045 SetWindowClassesDirty(WC_ROADVEH_LIST);
3046 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
3050 * Can this station be used by the given engine type?
3051 * @param engine_type the type of vehicles to test
3052 * @param st the station to test for
3053 * @return true if and only if the vehicle of the type can use this station.
3054 * @note For road vehicles the Vehicle is needed to determine whether it can
3055 * use the station. This function will return true for road vehicles
3056 * when at least one of the facilities is available.
3058 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
3060 const Engine *e = Engine::GetIfValid(engine_type);
3061 assert(e != nullptr);
3063 switch (e->type) {
3064 case VEH_TRAIN:
3065 return (st->facilities & FACIL_TRAIN) != 0;
3067 case VEH_ROAD:
3068 /* For road vehicles we need the vehicle to know whether it can actually
3069 * use the station, but if it doesn't have facilities for RVs it is
3070 * certainly not possible that the station can be used. */
3071 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
3073 case VEH_SHIP:
3074 return (st->facilities & FACIL_DOCK) != 0;
3076 case VEH_AIRCRAFT:
3077 return (st->facilities & FACIL_AIRPORT) != 0 &&
3078 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
3080 default:
3081 return false;
3086 * Can this station be used by the given vehicle?
3087 * @param v the vehicle to test
3088 * @param st the station to test for
3089 * @return true if and only if the vehicle can use this station.
3091 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
3093 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != nullptr;
3095 return CanVehicleUseStation(v->engine_type, st);
3099 * Get reason string why this station can't be used by the given vehicle.
3100 * @param v The vehicle to test.
3101 * @param st The station to test for.
3102 * @return The string explaining why the vehicle cannot use the station.
3104 StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st)
3106 switch (v->type) {
3107 case VEH_TRAIN:
3108 return STR_ERROR_NO_RAIL_STATION;
3110 case VEH_ROAD: {
3111 const RoadVehicle *rv = RoadVehicle::From(v);
3112 RoadStop *rs = st->GetPrimaryRoadStop(rv->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK);
3114 StringID err = rv->IsBus() ? STR_ERROR_NO_BUS_STATION : STR_ERROR_NO_TRUCK_STATION;
3116 for (; rs != nullptr; rs = rs->next) {
3117 /* Articulated vehicles cannot use bay road stops, only drive-through. Make sure the vehicle can actually use this bay stop */
3118 if (HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && IsBayRoadStopTile(rs->xy) && rv->HasArticulatedPart()) {
3119 err = STR_ERROR_NO_STOP_ARTICULATED_VEHICLE;
3120 continue;
3123 /* Bay stop errors take precedence, but otherwise the vehicle may not be compatible with the roadtype/tramtype of this station tile.
3124 * We give bay stop errors precedence because they are usually a bus sent to a tram station or vice versa. */
3125 if (!HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && err != STR_ERROR_NO_STOP_ARTICULATED_VEHICLE) {
3126 err = RoadTypeIsRoad(rv->roadtype) ? STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE : STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE;
3127 continue;
3131 return err;
3134 case VEH_SHIP:
3135 return STR_ERROR_NO_DOCK;
3137 case VEH_AIRCRAFT:
3138 if ((st->facilities & FACIL_AIRPORT) == 0) return STR_ERROR_NO_AIRPORT;
3139 if (v->GetEngine()->u.air.subtype & AIR_CTOL) {
3140 return STR_ERROR_AIRPORT_NO_PLANES;
3141 } else {
3142 return STR_ERROR_AIRPORT_NO_HELICOPTERS;
3145 default:
3146 return INVALID_STRING_ID;
3151 * Access the ground vehicle cache of the vehicle.
3152 * @pre The vehicle is a #GroundVehicle.
3153 * @return #GroundVehicleCache of the vehicle.
3155 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
3157 assert(this->IsGroundVehicle());
3158 if (this->type == VEH_TRAIN) {
3159 return &Train::From(this)->gcache;
3160 } else {
3161 return &RoadVehicle::From(this)->gcache;
3166 * Access the ground vehicle cache of the vehicle.
3167 * @pre The vehicle is a #GroundVehicle.
3168 * @return #GroundVehicleCache of the vehicle.
3170 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
3172 assert(this->IsGroundVehicle());
3173 if (this->type == VEH_TRAIN) {
3174 return &Train::From(this)->gcache;
3175 } else {
3176 return &RoadVehicle::From(this)->gcache;
3181 * Access the ground vehicle flags of the vehicle.
3182 * @pre The vehicle is a #GroundVehicle.
3183 * @return #GroundVehicleFlags of the vehicle.
3185 uint16_t &Vehicle::GetGroundVehicleFlags()
3187 assert(this->IsGroundVehicle());
3188 if (this->type == VEH_TRAIN) {
3189 return Train::From(this)->gv_flags;
3190 } else {
3191 return RoadVehicle::From(this)->gv_flags;
3196 * Access the ground vehicle flags of the vehicle.
3197 * @pre The vehicle is a #GroundVehicle.
3198 * @return #GroundVehicleFlags of the vehicle.
3200 const uint16_t &Vehicle::GetGroundVehicleFlags() const
3202 assert(this->IsGroundVehicle());
3203 if (this->type == VEH_TRAIN) {
3204 return Train::From(this)->gv_flags;
3205 } else {
3206 return RoadVehicle::From(this)->gv_flags;
3211 * Calculates the set of vehicles that will be affected by a given selection.
3212 * @param[in,out] set Set of affected vehicles.
3213 * @param v First vehicle of the selection.
3214 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
3215 * @pre \a set must be empty.
3216 * @post \a set will contain the vehicles that will be refitted.
3218 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8_t num_vehicles)
3220 if (v->type == VEH_TRAIN) {
3221 Train *u = Train::From(v);
3222 /* Only include whole vehicles, so start with the first articulated part */
3223 u = u->GetFirstEnginePart();
3225 /* Include num_vehicles vehicles, not counting articulated parts */
3226 for (; u != nullptr && num_vehicles > 0; num_vehicles--) {
3227 do {
3228 /* Include current vehicle in the selection. */
3229 include(set, u->index);
3231 /* If the vehicle is multiheaded, add the other part too. */
3232 if (u->IsMultiheaded()) include(set, u->other_multiheaded_part->index);
3234 u = u->Next();
3235 } while (u != nullptr && u->IsArticulatedPart());
3241 * Calculates the maximum weight of the ground vehicle when loaded.
3242 * @return Weight in tonnes
3244 uint32_t Vehicle::GetDisplayMaxWeight() const
3246 uint32_t max_weight = 0;
3248 for (const Vehicle *u = this; u != nullptr; u = u->Next()) {
3249 max_weight += u->GetMaxWeight();
3252 return max_weight;
3256 * Calculates the minimum power-to-weight ratio using the maximum weight of the ground vehicle
3257 * @return power-to-weight ratio in 10ths of hp(I) per tonne
3259 uint32_t Vehicle::GetDisplayMinPowerToWeight() const
3261 uint32_t max_weight = GetDisplayMaxWeight();
3262 if (max_weight == 0) return 0;
3263 return GetGroundVehicleCache()->cached_power * 10u / max_weight;
3267 * Checks if two vehicle chains have the same list of engines.
3268 * @param v1 First vehicle chain.
3269 * @param v1 Second vehicle chain.
3270 * @return True if same, false if different.
3272 bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2)
3274 while (true) {
3275 if (v1 == nullptr && v2 == nullptr) return true;
3276 if (v1 == nullptr || v2 == nullptr) return false;
3277 if (v1->GetEngine() != v2->GetEngine()) return false;
3278 v1 = v1->GetNextVehicle();
3279 v2 = v2->GetNextVehicle();
3284 * Checks if two vehicles have the same list of orders.
3285 * @param v1 First vehicles.
3286 * @param v1 Second vehicles.
3287 * @return True if same, false if different.
3289 bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2)
3291 const Order *o1 = v1->GetFirstOrder();
3292 const Order *o2 = v2->GetFirstOrder();
3293 while (true) {
3294 if (o1 == nullptr && o2 == nullptr) return true;
3295 if (o1 == nullptr || o2 == nullptr) return false;
3296 if (!o1->Equals(*o2)) return false;
3297 o1 = o1->next;
3298 o2 = o2->next;