Rework the way the ReinitSeparation command is called. The old way was way too danger...
[openttd-joker.git] / src / vehicle.cpp
blob5759b650a7b7fcbbe489305923fe681d4c9401d1
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file vehicle.cpp Base implementations of all vehicles. */
12 #include "stdafx.h"
14 #include "aircraft.h"
15 #include "articulated_vehicles.h"
16 #include "autoreplace_func.h"
17 #include "autoreplace_gui.h"
18 #include "bridge_map.h"
19 #include "command_func.h"
20 #include "company_func.h"
21 #include "date_func.h"
22 #include "depot_base.h"
23 #include "depot_func.h"
24 #include "depot_map.h"
25 #include "economy_base.h"
26 #include "effectvehicle_base.h"
27 #include "effectvehicle_func.h"
28 #include "error.h"
29 #include "gamelog.h"
30 #include "group_gui.h"
31 #include "newgrf_debug.h"
32 #include "newgrf_sound.h"
33 #include "newgrf_station.h"
34 #include "news_func.h"
35 #include "order_backup.h"
36 #include "roadstop_base.h"
37 #include "roadveh.h"
38 #include "ship.h"
39 #include "sound_func.h"
40 #include "spritecache.h"
41 #include "station_base.h"
42 #include "strings_func.h"
43 #include "tbtr_template_vehicle_func.h"
44 #include "timetable.h"
45 #include "tracerestrict.h"
46 #include "train.h"
47 #include "tunnel_map.h"
48 #include "vehicle_func.h"
49 #include "vehiclelist.h"
50 #include "viewport_func.h"
51 #include "zoom_func.h"
53 #include "ai/ai.hpp"
54 #include "blitter/factory.hpp"
55 #include "core/backup_type.hpp"
56 #include "core/pool_func.hpp"
57 #include "core/random_func.hpp"
58 #include "linkgraph/linkgraph.h"
59 #include "linkgraph/refresh.h"
60 #include "network/network.h"
61 #include "table/strings.h"
63 #include <algorithm>
65 #include "safeguards.h"
67 /* Number of bits in the hash to use from each vehicle coord */
68 static const uint GEN_HASHX_BITS = 6;
69 static const uint GEN_HASHY_BITS = 6;
71 /* Size of each hash bucket */
72 static const uint GEN_HASHX_BUCKET_BITS = 7;
73 static const uint GEN_HASHY_BUCKET_BITS = 6;
75 /* Compute hash for vehicle coord */
76 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
77 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
78 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
80 /* Maximum size until hash repeats */
81 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_LVL_SHIFT);
82 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_LVL_SHIFT);
84 /* Increments to reach next bucket in hash table */
85 static const int GEN_HASHX_INC = 1;
86 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
88 /* Mask to wrap-around buckets */
89 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
90 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
92 VehicleID _new_vehicle_id;
93 uint16 _returned_refit_capacity; ///< Stores the capacity after a refit operation.
94 uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refit operation (Aircraft only).
97 /** The pool with all our precious vehicles. */
98 VehiclePool _vehicle_pool("Vehicle");
99 INSTANTIATE_POOL_METHODS(Vehicle)
103 * Determine shared bounds of all sprites.
104 * @param [out] bounds Shared bounds.
106 Rect16 VehicleSpriteSeq::GetBounds() const
108 Rect16 bounds;
109 bounds.left = bounds.top = bounds.right = bounds.bottom = 0;
111 for (uint i = 0; i < this->count; ++i) {
112 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
114 if (i == 0) {
115 bounds.left = spr->x_offs;
116 bounds.top = spr->y_offs;
117 bounds.right = spr->width + spr->x_offs - 1;
118 bounds.bottom = spr->height + spr->y_offs - 1;
119 } else {
120 if (spr->x_offs < bounds.left) bounds.left = spr->x_offs;
121 if (spr->y_offs < bounds.top) bounds.top = spr->y_offs;
122 int right = spr->width + spr->x_offs - 1;
123 int bottom = spr->height + spr->y_offs - 1;
124 if (right > bounds.right) bounds.right = right;
125 if (bottom > bounds.bottom) bounds.bottom = bottom;
129 return bounds;
133 * Draw the sprite sequence.
134 * @param x X position
135 * @param y Y position
136 * @param default_pal Vehicle palette
137 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
139 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
141 for (uint i = 0; i < this->count; ++i) {
142 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
143 DrawSprite(this->seq[i].sprite, pal, x, y);
148 * Function to tell if a vehicle needs to be autorenewed
149 * @param *c The vehicle owner
150 * @param use_renew_setting Should the company renew setting be considered?
151 * @return true if the vehicle is old enough for replacement
153 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
155 /* We can always generate the Company pointer when we have the vehicle.
156 * However this takes time and since the Company pointer is often present
157 * when this function is called then it's faster to pass the pointer as an
158 * argument rather than finding it again. */
159 assert(c == Company::Get(this->owner));
161 if (use_renew_setting && !c->settings.engine_renew) return false;
162 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
164 /* Only engines need renewing */
165 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
167 return true;
171 * Service a vehicle and all subsequent vehicles in the consist
173 * @param *v The vehicle or vehicle chain being serviced
175 void VehicleServiceInDepot(Vehicle *v)
177 assert(v != nullptr);
178 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
180 do {
181 v->date_of_last_service = _date;
182 v->breakdowns_since_last_service = 0;
183 v->reliability = v->GetEngine()->reliability;
184 /* Prevent vehicles from breaking down directly after exiting the depot. */
185 v->breakdown_chance = 0;
186 v->breakdown_ctr = 0;
187 v = v->Next();
188 } while (v != nullptr && v->HasEngineType());
192 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
194 * @see NeedsAutomaticServicing()
195 * @return true if the vehicle should go to a depot if a opportunity presents itself.
197 bool Vehicle::NeedsServicing() const
199 /* Stopped or crashed vehicles will not move, as such making unmovable
200 * vehicles to go for service is lame. */
201 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
203 /* Are we ready for the next service cycle? */
204 const Company *c = Company::Get(this->owner);
206 if (this->ServiceIntervalIsPercent() ?
207 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
208 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
209 return false;
212 /* Do we even have any depots/hangars */
213 uint rail_pices = 0;
214 uint road_pieces = 0;
216 for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pices += c->infrastructure.rail[i];
217 for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i];
219 if ((this->type == VEH_TRAIN && rail_pices == 0) ||
220 (this->type == VEH_ROAD && road_pieces == 0) ||
221 (this->type == VEH_SHIP && c->infrastructure.water == 0) ||
222 (this->type == VEH_AIRCRAFT && c->infrastructure.airport == 0)) {
223 return false;
226 /* If we're servicing anyway, because we have not disabled servicing when
227 * there are no breakdowns or we are playing with breakdowns, bail out. */
228 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
229 _settings_game.difficulty.vehicle_breakdowns != 0) {
230 return true;
233 /* Is vehicle old and renewing is enabled */
234 if (this->NeedsAutorenewing(c, true)) {
235 return true;
238 /* Test whether there is some pending autoreplace.
239 * Note: We do this after the service-interval test.
240 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
241 bool pending_replace = false;
242 Money needed_money = c->settings.engine_renew_money;
243 if (needed_money > c->money) return false;
245 for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) {
246 bool replace_when_old = false;
247 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
249 /* Check engine availability */
250 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
251 /* Is the vehicle old if we are not always replacing? */
252 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
254 /* Check refittability */
255 uint32 available_cargo_types, union_mask;
256 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
257 /* Is there anything to refit? */
258 if (union_mask != 0) {
259 CargoID cargo_type;
260 /* We cannot refit to mixed cargoes in an automated way */
261 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
263 /* Did the old vehicle carry anything? */
264 if (cargo_type != CT_INVALID) {
265 /* We can't refit the vehicle to carry the cargo we want */
266 if (!HasBit(available_cargo_types, cargo_type)) continue;
270 /* Check money.
271 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
272 pending_replace = true;
273 needed_money += 2 * Engine::Get(new_engine)->GetCost();
274 if (needed_money > c->money) return false;
277 return pending_replace;
281 * Checks if the current order should be interrupted for a service-in-depot order.
282 * @see NeedsServicing()
283 * @return true if the current order should be interrupted.
285 bool Vehicle::NeedsAutomaticServicing() const
287 if (this->HasDepotOrder()) return false;
288 if (this->current_order.IsType(OT_LOADING)) return false;
289 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
291 return NeedsServicing();
294 uint Vehicle::Crash(bool flooded)
296 assert((this->vehstatus & VS_CRASHED) == 0);
297 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
299 uint pass = 0;
300 /* Stop the vehicle. */
301 if (this->IsPrimaryVehicle())
302 this->vehstatus |= VS_STOPPED;
303 /* crash all wagons, and count passengers */
304 for (Vehicle *v = this; v != nullptr; v = v->Next()) {
305 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
306 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
307 v->vehstatus |= VS_CRASHED;
308 v->MarkAllViewportsDirty();
311 /* Dirty some windows */
312 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
313 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
314 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
315 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
317 delete this->cargo_payment;
318 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
320 return RandomRange(pass + 1); // Randomise deceased passengers.
323 /** Marks the separation of this vehicle's order list invalid. */
324 void Vehicle::MarkSeparationInvalid()
326 if (this->orders.list != nullptr) this->orders.list->MarkSeparationInvalid();
329 /** Sets new separation settings for this vehicle's shared orders. */
330 void Vehicle::SetSepSettings(TTSepMode Mode, uint Parameter)
332 if (this->orders.list != nullptr) this->orders.list->SetSepSettings(Mode, Parameter);
336 * Get whether a the vehicle should be drawn (i.e. if it isn't hidden, or it is in a tunnel but being shown transparently)
337 * @return whether to show vehicle
339 bool Vehicle::IsDrawn() const
341 return !(HasBit(this->subtype, GVSF_VIRTUAL)) && (!(this->vehstatus & VS_HIDDEN) ||
342 (IsTransparencySet(TO_TUNNELS) &&
343 ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) ||
344 (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE))));
348 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
349 * @param engine The engine that caused the problem
350 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
351 * @param part2 Part 2 of the error message, taking the engine as parameter 2
352 * @param bug_type Flag to check and set in grfconfig
353 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
355 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
357 const Engine *e = Engine::Get(engine);
358 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
360 /* Missing GRF. Nothing useful can be done in this situation. */
361 if (grfconfig == nullptr) return;
363 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
364 SetBit(grfconfig->grf_bugs, bug_type);
365 SetDParamStr(0, grfconfig->GetName());
366 SetDParam(1, engine);
367 ShowErrorMessage(part1, part2, WL_CRITICAL);
368 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
371 /* debug output */
372 char buffer[512];
374 SetDParamStr(0, grfconfig->GetName());
375 GetString(buffer, part1, lastof(buffer));
376 DEBUG(grf, 0, "%s", buffer + 3);
378 SetDParam(1, engine);
379 GetString(buffer, part2, lastof(buffer));
380 DEBUG(grf, 0, "%s", buffer + 3);
384 * Logs a bug in GRF and shows a warning message if this
385 * is for the first time this happened.
386 * @param u first vehicle of chain
388 void VehicleLengthChanged(const Vehicle *u)
390 /* show a warning once for each engine in whole game and once for each GRF after each game load */
391 const Engine *engine = u->GetEngine();
392 uint32 grfid = engine->grf_prop.grffile->grfid;
393 GRFConfig *grfconfig = GetGRFConfig(grfid);
394 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
395 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
400 * Vehicle constructor.
401 * @param type Type of the new vehicle.
403 Vehicle::Vehicle(VehicleType type)
405 this->type = type;
406 this->coord.left = INVALID_COORD;
407 this->group_id = DEFAULT_GROUP;
408 this->fill_percent_te_id = INVALID_TE_ID;
409 this->first = this;
410 this->colourmap = PAL_NONE;
411 this->cargo_age_counter = 1;
412 this->last_station_visited = INVALID_STATION;
413 this->last_loading_station = INVALID_STATION;
417 * Get a value for a vehicle's random_bits.
418 * @return A random value from 0 to 255.
420 byte VehicleRandomBits()
422 return GB(Random(), 0, 8);
425 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
426 * lookup times at the expense of memory usage. */
427 const int HASH_BITS = 7;
428 const int HASH_SIZE = 1 << HASH_BITS;
429 const int HASH_MASK = HASH_SIZE - 1;
430 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
431 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
433 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
434 * Profiling results show that 0 is fastest. */
435 const int HASH_RES = 0;
437 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
439 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
441 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
442 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
443 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
444 for (; v != nullptr; v = v->hash_tile_next) {
445 Vehicle *a = proc(v, data);
446 if (find_first && a != nullptr) return a;
448 if (x == xu) break;
450 if (y == yu) break;
453 return nullptr;
458 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
459 * @note Do not call this function directly!
460 * @param x The X location on the map
461 * @param y The Y location on the map
462 * @param data Arbitrary data passed to proc
463 * @param proc The proc that determines whether a vehicle will be "found".
464 * @param find_first Whether to return on the first found or iterate over
465 * all vehicles
466 * @return the best matching or first vehicle (depending on find_first).
468 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
470 const int COLL_DIST = 6;
472 /* Hash area to scan is from xl,yl to xu,yu */
473 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
474 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
475 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
476 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
478 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
482 * Find a vehicle from a specific location. It will call proc for ALL vehicles
483 * on the tile and YOU must make SURE that the "best one" is stored in the
484 * data value and is ALWAYS the same regardless of the order of the vehicles
485 * where proc was called on!
486 * When you fail to do this properly you create an almost untraceable DESYNC!
487 * @note The return value of proc will be ignored.
488 * @note Use this when you have the intention that all vehicles
489 * should be iterated over.
490 * @param x The X location on the map
491 * @param y The Y location on the map
492 * @param data Arbitrary data passed to proc
493 * @param proc The proc that determines whether a vehicle will be "found".
495 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
497 VehicleFromPosXY(x, y, data, proc, false);
501 * Checks whether a vehicle in on a specific location. It will call proc for
502 * vehicles until it returns non-nullptr.
503 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
504 * should be iterated over.
505 * @param x The X location on the map
506 * @param y The Y location on the map
507 * @param data Arbitrary data passed to proc
508 * @param proc The proc that determines whether a vehicle will be "found".
509 * @return True if proc returned non-nullptr.
511 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
513 return VehicleFromPosXY(x, y, data, proc, true) != nullptr;
517 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
518 * @note Do not call this function directly!
519 * @param tile The location on the map
520 * @param data Arbitrary data passed to \a proc.
521 * @param proc The proc that determines whether a vehicle will be "found".
522 * @param find_first Whether to return on the first found or iterate over
523 * all vehicles
524 * @return the best matching or first vehicle (depending on find_first).
526 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
528 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
529 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
531 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
532 for (; v != nullptr; v = v->hash_tile_next) {
533 if (v->tile != tile) continue;
535 Vehicle *a = proc(v, data);
536 if (find_first && a != nullptr) return a;
539 return nullptr;
543 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
544 * on the tile and YOU must make SURE that the "best one" is stored in the
545 * data value and is ALWAYS the same regardless of the order of the vehicles
546 * where proc was called on!
547 * When you fail to do this properly you create an almost untraceable DESYNC!
548 * @note The return value of \a proc will be ignored.
549 * @note Use this function when you have the intention that all vehicles
550 * should be iterated over.
551 * @param tile The location on the map
552 * @param data Arbitrary data passed to \a proc.
553 * @param proc The proc that determines whether a vehicle will be "found".
555 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
557 VehicleFromPos(tile, data, proc, false);
561 * Checks whether a vehicle is on a specific location. It will call \a proc for
562 * vehicles until it returns non-nullptr.
563 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
564 * should be iterated over.
565 * @param tile The location on the map
566 * @param data Arbitrary data passed to \a proc.
567 * @param proc The \a proc that determines whether a vehicle will be "found".
568 * @return True if proc returned non-nullptr.
570 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
572 return VehicleFromPos(tile, data, proc, true) != nullptr;
576 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
577 * @param v Vehicle to examine.
578 * @param data Pointer to height data.
579 * @return \a v if conditions are met, else \c nullptr.
581 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
583 int z = *(int*)data;
585 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return nullptr;
586 if (v->z_pos > z) return nullptr;
588 return v;
592 * Ensure there is no vehicle at the ground at the given position.
593 * @param tile Position to examine.
594 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
596 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
598 int z = GetTileMaxPixelZ(tile);
600 /* Value v is not safe in MP games, however, it is used to generate a local
601 * error message only (which may be different for different machines).
602 * Such a message does not affect MP synchronisation.
604 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
605 if (v != nullptr) return CommandError(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
606 return CommandCost();
609 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
610 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
612 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return nullptr;
613 if (v == (const Vehicle *)data) return nullptr;
615 return v;
619 * Finds vehicle in tunnel / bridge
620 * @param tile first end
621 * @param endtile second end
622 * @param ignore Ignore this vehicle when searching
623 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
625 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
627 /* Value v is not safe in MP games, however, it is used to generate a local
628 * error message only (which may be different for different machines).
629 * Such a message does not affect MP synchronisation.
631 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
632 if (v == nullptr) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
634 if (v != nullptr) return CommandError(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
635 return CommandCost();
638 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
640 TrackBits rail_bits = *(TrackBits *)data;
642 if (v->type != VEH_TRAIN) return nullptr;
644 Train *t = Train::From(v);
645 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return nullptr;
647 return v;
651 * Tests if a vehicle interacts with the specified track bits.
652 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
654 * @param tile The tile.
655 * @param track_bits The track bits.
656 * @return \c true if no train that interacts, is found. \c false if a train is found.
658 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
660 /* Value v is not safe in MP games, however, it is used to generate a local
661 * error message only (which may be different for different machines).
662 * Such a message does not affect MP synchronisation.
664 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
665 if (v != nullptr) return CommandError(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
666 return CommandCost();
669 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
671 Vehicle **old_hash = v->hash_tile_current;
672 Vehicle **new_hash;
674 if (remove) {
675 new_hash = nullptr;
676 } else {
677 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
678 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
679 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
682 if (old_hash == new_hash) return;
684 /* Remove from the old position in the hash table */
685 if (old_hash != nullptr) {
686 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
687 *v->hash_tile_prev = v->hash_tile_next;
690 /* Insert vehicle at beginning of the new position in the hash table */
691 if (new_hash != nullptr) {
692 v->hash_tile_next = *new_hash;
693 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
694 v->hash_tile_prev = new_hash;
695 *new_hash = v;
698 /* Remember current hash position */
699 v->hash_tile_current = new_hash;
702 static Vehicle *_vehicle_viewport_hash[1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)];
704 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
706 Vehicle **old_hash, **new_hash;
707 int old_x = v->coord.left;
708 int old_y = v->coord.top;
710 new_hash = (x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(x, y)];
711 old_hash = (old_x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
713 if (old_hash == new_hash) return;
715 /* remove from hash table? */
716 if (old_hash != nullptr) {
717 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
718 *v->hash_viewport_prev = v->hash_viewport_next;
721 /* insert into hash table? */
722 if (new_hash != nullptr) {
723 v->hash_viewport_next = *new_hash;
724 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
725 v->hash_viewport_prev = new_hash;
726 *new_hash = v;
730 void ResetVehicleHash()
732 Vehicle *v;
733 FOR_ALL_VEHICLES(v) { v->hash_tile_current = nullptr; }
734 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
735 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
738 void ResetVehicleColourMap()
740 Vehicle *v;
741 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
745 * List of vehicles that should check for autoreplace this tick.
746 * Mapping of vehicle -> leave depot immediately after autoreplace.
748 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
749 static AutoreplaceMap _vehicles_to_autoreplace;
752 * List of vehicles that are issued for template replacement this tick.
753 * Mapping is {vehicle : leave depot after replacement}
755 typedef SmallMap<Train *, bool, 4> TemplateReplacementMap;
756 static TemplateReplacementMap _vehicles_to_templatereplace;
758 void InitializeVehicles()
760 _vehicles_to_autoreplace.Reset();
761 ResetVehicleHash();
764 uint CountVehiclesInChain(const Vehicle *v)
766 uint count = 0;
767 do count++; while ((v = v->Next()) != nullptr);
768 return count;
772 * Check if a vehicle is counted in num_engines in each company struct
773 * @return true if the vehicle is counted in num_engines
775 bool Vehicle::IsEngineCountable() const
777 if (HasBit(this->subtype, GVSF_VIRTUAL)) return false;
778 switch (this->type) {
779 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
780 case VEH_TRAIN:
781 return !this->IsArticulatedPart() && // tenders and other articulated parts
782 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
783 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
784 case VEH_SHIP: return true;
785 default: return false; // Only count company buildable vehicles
790 * Check whether Vehicle::engine_type has any meaning.
791 * @return true if the vehicle has a useable engine type.
793 bool Vehicle::HasEngineType() const
795 switch (this->type) {
796 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
797 case VEH_TRAIN:
798 case VEH_ROAD:
799 case VEH_SHIP: return true;
800 default: return false;
805 * Retrieves the engine of the vehicle.
806 * @return Engine of the vehicle.
807 * @pre HasEngineType() == true
809 const Engine *Vehicle::GetEngine() const
811 return Engine::Get(this->engine_type);
815 * Retrieve the NewGRF the vehicle is tied to.
816 * This is the GRF providing the Action 3 for the engine type.
817 * @return NewGRF associated to the vehicle.
819 const GRFFile *Vehicle::GetGRF() const
821 return this->GetEngine()->GetGRF();
825 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
826 * This is the GRF providing the Action 3 for the engine type.
827 * @return GRF ID of the associated NewGRF.
829 uint32 Vehicle::GetGRFID() const
831 return this->GetEngine()->GetGRFID();
835 * Handle the pathfinding result, especially the lost status.
836 * If the vehicle is now lost and wasn't previously fire an
837 * event to the AIs and a news message to the user. If the
838 * vehicle is not lost anymore remove the news message.
839 * @param path_found Whether the vehicle has a path to its destination.
841 void Vehicle::HandlePathfindingResult(bool path_found)
843 if (path_found) {
844 /* Route found, is the vehicle marked with "lost" flag? */
845 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
847 /* Clear the flag as the PF's problem was solved. */
848 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
849 /* Delete the news item. */
850 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
851 return;
854 /* Were we already lost? */
855 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
857 /* It is first time the problem occurred, set the "lost" flag. */
858 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
859 /* Notify user about the event. */
860 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
861 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
862 SetDParam(0, this->index);
863 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
867 /** Destroy all stuff that (still) needs the virtual functions to work properly */
868 void Vehicle::PreDestructor()
870 if (CleaningPool()) return;
872 if (Station::IsValidID(this->last_station_visited)) {
873 Station *st = Station::Get(this->last_station_visited);
874 st->loading_vehicles.erase(std::remove(st->loading_vehicles.begin(), st->loading_vehicles.end(), this), st->loading_vehicles.end());
876 HideFillingPercent(&this->fill_percent_te_id);
877 this->CancelReservation(INVALID_STATION, st);
878 delete this->cargo_payment;
879 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
882 if (this->IsEngineCountable()) {
883 GroupStatistics::CountEngine(this, -1);
884 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
885 GroupStatistics::UpdateAutoreplace(this->owner);
887 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
888 DeleteGroupHighlightOfVehicle(this);
889 if (this->type == VEH_TRAIN) {
890 extern void DeleteTraceRestrictSlotHighlightOfVehicle(const Vehicle *v);
892 DeleteTraceRestrictSlotHighlightOfVehicle(this);
896 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
897 Aircraft *a = Aircraft::From(this);
898 Station *st = GetTargetAirportIfValid(a);
899 if (st != nullptr) {
900 const AirportFTA *layout = st->airport.GetFTA()->layout;
901 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
906 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
907 RoadVehicle *v = RoadVehicle::From(this);
908 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
909 /* Leave the drive through roadstop, when you have not already left it. */
910 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
914 if (this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_HAVE_SLOT)) {
915 TraceRestrictRemoveVehicleFromAllSlots(this->index);
916 ClrBit(Train::From(this)->flags, VRF_HAVE_SLOT);
919 if (this->Previous() == nullptr) {
920 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
923 if (this->IsPrimaryVehicle()) {
924 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
925 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
926 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
927 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
928 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
929 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, this->index);
930 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, this->index);
931 SetWindowDirty(WC_COMPANY, this->owner);
932 OrderBackup::ClearVehicle(this);
934 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
936 this->cargo.Truncate();
937 this->DeleteVehicleOrders();
938 DeleteDepotHighlightOfVehicle(this);
940 extern void StopGlobalFollowVehicle(const Vehicle *v);
941 StopGlobalFollowVehicle(this);
943 ReleaseDisastersTargetingVehicle(this->index);
946 Vehicle::~Vehicle()
948 if (CleaningPool()) {
949 this->cargo.OnCleanPool();
950 return;
953 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
954 * it may happen that vehicle chain is deleted when visible */
955 if (this->IsDrawn()) this->MarkAllViewportsDirty();
957 Vehicle *v = this->Next();
958 this->SetNext(nullptr);
960 delete v;
962 UpdateVehicleTileHash(this, true);
963 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
964 DeleteVehicleNews(this->index, INVALID_STRING_ID);
965 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
969 * Adds a vehicle to the list of vehicles that visited a depot this tick
970 * @param vehicle vehicle to add
972 void VehicleEnteredDepotThisTick(Vehicle* vehicle)
974 assert(vehicle != nullptr);
975 assert(vehicle != nullptr);
977 // Template Replacement Setup stuff
979 const bool stay_in_depot = vehicle->current_order.GetType() == OT_GOTO_DEPOT &&
980 vehicle->current_order.GetDepotActionType() != ODATF_SERVICE_ONLY;
982 const TemplateReplacement* template_replacement = GetTemplateReplacementByGroupID(vehicle->group_id);
984 if (template_replacement != nullptr) {
985 _vehicles_to_templatereplace[Train::From(vehicle)] = stay_in_depot;
986 } else {
987 // Moved the assignment for auto replacement here to prevent auto replacement
988 // from happening if template replacement is also scheduled.
989 // Vehicle should stop in the depot if it was in 'stopping' state.
990 _vehicles_to_autoreplace[vehicle] = !(vehicle->vehstatus & VS_STOPPED);
994 // Manual depot order stuff
996 const bool has_manual_depot_order = HasBit(vehicle->vehicle_flags, VF_SHOULD_GOTO_DEPOT) ||
997 HasBit(vehicle->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT) ||
998 (vehicle->current_order.GetType() == OT_GOTO_DEPOT &&
999 vehicle->current_order.GetDepotOrderType() == ODTF_MANUAL);
1001 const Order* real_current_order = vehicle->GetOrder(vehicle->cur_real_order_index);
1003 const bool current_order_needs_change = real_current_order == nullptr ||
1004 real_current_order->GetType() != OT_GOTO_DEPOT ||
1005 real_current_order->GetDestination() != vehicle->current_order.GetDestination();
1007 if (vehicle->HasOrdersList() && has_manual_depot_order && current_order_needs_change) {
1008 std::vector<int> depot_order_indices;
1010 for (int i = 0; i < vehicle->GetNumOrders(); ++i) {
1011 const Order* order = vehicle->GetOrder(i);
1013 assert(order != nullptr);
1015 if (order->GetType() == OT_GOTO_DEPOT && order->GetDestination() == vehicle->current_order.GetDestination()) {
1016 depot_order_indices.push_back(i);
1020 if (!depot_order_indices.empty()) {
1021 const auto next_depot_order = std::find_if(depot_order_indices.begin(), depot_order_indices.end(), [&](int index)
1023 return index > vehicle->cur_real_order_index;
1026 const int new_order_index = next_depot_order != depot_order_indices.end() ?
1027 *next_depot_order :
1028 *depot_order_indices.begin();
1030 vehicle->cur_implicit_order_index = new_order_index;
1031 vehicle->cur_real_order_index = new_order_index;
1033 // We're skipping to the next depot order. Travel times will be wrong.
1034 vehicle->cur_timetable_order_index = INVALID_VEH_ORDER_ID;
1036 InvalidateVehicleOrder(vehicle, 0);
1040 ClrBit(vehicle->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
1041 ClrBit(vehicle->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
1044 // We ALWAYS set the stopped state. Even when the vehicle does not plan on
1045 // stopping in the depot, so we stop it to ensure that it will not reserve
1046 // the path out of the depot before we might autoreplace it to a different
1047 // engine. The new engine would not own the reserved path we store that we
1048 // stopped the vehicle, so autoreplace can start it again.
1049 vehicle->vehstatus |= VS_STOPPED;
1053 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
1054 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
1055 * so each day, all vehicles are processes in DAY_TICKS steps.
1057 static void RunVehicleDayProc()
1059 if (_game_mode != GM_NORMAL) return;
1061 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1062 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
1063 Vehicle *v = Vehicle::Get(i);
1064 if (v == nullptr) continue;
1066 /* Call the 32-day callback if needed */
1067 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
1068 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
1069 if (callback != CALLBACK_FAILED) {
1070 if (HasBit(callback, 0)) {
1071 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
1074 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1075 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1076 if (callback != 0) v->First()->MarkDirty();
1078 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
1082 /* This is called once per day for each vehicle, but not in the first tick of the day */
1083 v->OnNewDay();
1087 static void ShowAutoReplaceAdviceMessage(const CommandCost &res, const Vehicle *v)
1089 StringID error_message = res.GetErrorMessage();
1090 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) return;
1092 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1094 StringID message;
1095 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1096 message = error_message;
1097 } else {
1098 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1101 SetDParam(0, v->index);
1102 SetDParam(1, error_message);
1103 AddVehicleAdviceNewsItem(message, v->index);
1106 void CallVehicleTicks()
1108 _vehicles_to_autoreplace.Clear();
1109 _vehicles_to_templatereplace.Clear();
1111 RunVehicleDayProc();
1113 Station *st;
1114 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
1116 Vehicle *v;
1117 FOR_ALL_VEHICLES(v) {
1118 /* Vehicle could be deleted in this tick */
1119 if (!v->Tick()) {
1120 assert(Vehicle::Get(vehicle_index) == nullptr);
1121 continue;
1124 assert(Vehicle::Get(vehicle_index) == v);
1126 switch (v->type) {
1127 default: break;
1129 case VEH_TRAIN:
1130 case VEH_ROAD:
1131 case VEH_AIRCRAFT:
1132 case VEH_SHIP: {
1133 Vehicle *front = v->First();
1135 if (v->vcache.cached_cargo_age_period != 0) {
1136 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
1137 if (--v->cargo_age_counter == 0) {
1138 v->cargo.AgeCargo();
1139 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
1143 /* Do not play any sound when crashed */
1144 if (front->vehstatus & VS_CRASHED) continue;
1146 /* Do not play any sound when in depot or tunnel */
1147 if (v->vehstatus & VS_HIDDEN) continue;
1149 /* Do not play any sound when stopped */
1150 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1152 /* Check vehicle type specifics */
1153 switch (v->type) {
1154 case VEH_TRAIN:
1155 if (Train::From(v)->IsWagon()) continue;
1156 break;
1158 case VEH_ROAD:
1159 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1160 break;
1162 case VEH_AIRCRAFT:
1163 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1164 break;
1166 default:
1167 break;
1170 v->motion_counter += front->cur_speed;
1171 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1172 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1174 /* Play an alternating running sound every 16 ticks */
1175 if (GB(v->tick_counter, 0, 4) == 0) {
1176 /* Play running sound when speed > 0 and not braking */
1177 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1178 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1181 break;
1186 /* do Template Replacement */
1187 Backup<CompanyByte> tmpl_cur_company(_current_company, FILE_LINE);
1188 for (TemplateReplacementMap::iterator it = _vehicles_to_templatereplace.Begin(); it != _vehicles_to_templatereplace.End(); it++) {
1189 Train *t = it->first;
1191 _vehicles_to_autoreplace.Erase(t);
1193 /* Store the position of the effect as the vehicle pointer will become invalid later */
1194 int x = t->x_pos;
1195 int y = t->y_pos;
1196 int z = t->z_pos;
1198 tmpl_cur_company.Change(t->owner);
1200 bool stayInDepot = it->second;
1202 it->first->vehstatus |= VS_STOPPED;
1203 CommandCost res = DoCommand(t->tile, t->index, stayInDepot ? 1 : 0, DC_EXEC, CMD_TEMPLATE_REPLACE_VEHICLE);
1205 if (res.Succeeded()) {
1206 VehicleID t_new = _new_vehicle_id;
1207 t = Train::From(Vehicle::Get(t_new));
1208 const Company *c = Company::Get(_current_company);
1209 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1210 CommandCost res2 = DoCommand(0, t_new, 1, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1211 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1212 if (res2.Succeeded() || res.GetCost() == 0) res.AddCost(res2);
1215 if (!IsLocalCompany()) continue;
1217 if (res.Succeeded()) {
1218 if (res.GetCost() != 0) {
1219 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1221 continue;
1224 ShowAutoReplaceAdviceMessage(res, t);
1226 tmpl_cur_company.Restore();
1228 /* do Auto Replacement */
1229 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1230 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1231 v = it->first;
1232 /* Autoreplace needs the current company set as the vehicle owner */
1233 cur_company.Change(v->owner);
1235 if (v->type == VEH_TRAIN) {
1236 assert(!_vehicles_to_templatereplace.Contains(Train::From(v)));
1239 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1240 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1241 * they are already leaving the depot again before being replaced. */
1242 if (it->second) v->vehstatus &= ~VS_STOPPED;
1244 /* Store the position of the effect as the vehicle pointer will become invalid later */
1245 int x = v->x_pos;
1246 int y = v->y_pos;
1247 int z = v->z_pos;
1249 const Company *c = Company::Get(_current_company);
1250 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1251 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1252 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1254 if (!IsLocalCompany()) continue;
1256 if (res.Succeeded()) {
1257 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1258 continue;
1261 ShowAutoReplaceAdviceMessage(res, v);
1263 cur_company.Restore();
1267 * Add vehicle sprite for drawing to the screen.
1268 * @param v Vehicle to draw.
1270 static void DoDrawVehicle(const Vehicle *v)
1272 PaletteID pal = PAL_NONE;
1274 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1276 /* Check whether the vehicle shall be transparent due to the game state */
1277 bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN)) != 0;
1279 if (v->type == VEH_EFFECT) {
1280 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1281 * However, transparent smoke and bubbles look weird, so always hide them. */
1282 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1283 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1286 StartSpriteCombine();
1287 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1288 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1289 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1290 AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1291 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1293 EndSpriteCombine();
1296 struct ViewportHashBound {
1297 int xl, xu, yl, yu;
1300 static ViewportHashBound GetViewportHashBound(int l, int r, int t, int b) {
1301 int xl = (l - (70 * ZOOM_LVL_BASE)) >> (7 + ZOOM_LVL_SHIFT);
1302 int xu = (r ) >> (7 + ZOOM_LVL_SHIFT);
1303 /* compare after shifting instead of before, so that lower bits don't affect comparison result */
1304 if (xu - xl < (1 << 6)) {
1305 xl &= 0x3F;
1306 xu &= 0x3F;
1307 } else {
1308 /* scan whole hash row */
1309 xl = 0;
1310 xu = 0x3F;
1313 int yl = (t - (70 * ZOOM_LVL_BASE)) >> (6 + ZOOM_LVL_SHIFT);
1314 int yu = (b ) >> (6 + ZOOM_LVL_SHIFT);
1315 /* compare after shifting instead of before, so that lower bits don't affect comparison result */
1316 if (yu - yl < (1 << 6)) {
1317 yl = (yl & 0x3F) << 6;
1318 yu = (yu & 0x3F) << 6;
1319 } else {
1320 /* scan whole column */
1321 yl = 0;
1322 yu = 0x3F << 6;
1324 return { xl, xu, yl, yu };
1328 * Add the vehicle sprites that should be drawn at a part of the screen.
1329 * @param dpi Rectangle being drawn.
1331 void ViewportAddVehicles(DrawPixelInfo *dpi)
1333 /* The bounding rectangle */
1334 const int l = dpi->left;
1335 const int r = dpi->left + dpi->width;
1336 const int t = dpi->top;
1337 const int b = dpi->top + dpi->height;
1339 /* The hash area to scan */
1340 const ViewportHashBound vhb = GetViewportHashBound(l, r, t, b);
1342 for (int y = vhb.yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1343 for (int x = vhb.xl;; x = (x + 1) & 0x3F) {
1344 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1346 while (v != nullptr) {
1347 if (v->IsDrawn() &&
1348 l <= v->coord.right &&
1349 t <= v->coord.bottom &&
1350 r >= v->coord.left &&
1351 b >= v->coord.top) {
1352 DoDrawVehicle(v);
1354 v = v->hash_viewport_next;
1357 if (x == vhb.xu) break;
1360 if (y == vhb.yu) break;
1364 void ViewportMapDrawVehicles(DrawPixelInfo *dpi)
1366 /* The bounding rectangle */
1367 const int l = dpi->left;
1368 const int r = dpi->left + dpi->width;
1369 const int t = dpi->top;
1370 const int b = dpi->top + dpi->height;
1372 /* The hash area to scan */
1373 const ViewportHashBound vhb = GetViewportHashBound(l, r, t, b);
1375 const int w = UnScaleByZoom(dpi->width, dpi->zoom);
1376 const int h = UnScaleByZoom(dpi->height, dpi->zoom);
1377 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
1378 for (int y = vhb.yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1379 for (int x = vhb.xl;; x = (x + 1) & 0x3F) {
1380 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1382 while (v != nullptr) {
1383 if (!(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) && (v->type != VEH_EFFECT)) {
1384 Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
1385 const int pixel_x = UnScaleByZoomLower(pt.x - dpi->left, dpi->zoom);
1386 if (IsInsideMM(pixel_x, 0, w)) {
1387 const int pixel_y = UnScaleByZoomLower(pt.y - dpi->top, dpi->zoom);
1388 if (IsInsideMM(pixel_y, 0, h))
1389 blitter->SetPixel(dpi->dst_ptr, pixel_x, pixel_y, PC_WHITE);
1392 v = v->hash_viewport_next;
1395 if (x == vhb.xu) break;
1398 if (y == vhb.yu) break;
1403 * Find the vehicle close to the clicked coordinates.
1404 * @param vp Viewport clicked in.
1405 * @param x X coordinate in the viewport.
1406 * @param y Y coordinate in the viewport.
1407 * @return Closest vehicle, or \c nullptr if none found.
1409 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1411 Vehicle *found = nullptr, *v;
1412 uint dist, best_dist = UINT_MAX;
1414 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return nullptr;
1416 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1417 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1419 FOR_ALL_VEHICLES(v) {
1420 if (((v->vehstatus & VS_UNCLICKABLE) == 0) && v->IsDrawn() &&
1421 x >= v->coord.left && x <= v->coord.right &&
1422 y >= v->coord.top && y <= v->coord.bottom) {
1424 dist = max(
1425 abs(((v->coord.left + v->coord.right) >> 1) - x),
1426 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1429 if (dist < best_dist) {
1430 found = v;
1431 best_dist = dist;
1436 return found;
1440 * Decrease the value of a vehicle.
1441 * @param v %Vehicle to devaluate.
1443 void DecreaseVehicleValue(Vehicle *v)
1445 v->value -= v->value >> 8;
1446 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1449 static const byte _breakdown_chance[64] = {
1450 3, 3, 3, 3, 3, 3, 3, 3,
1451 4, 4, 5, 5, 6, 6, 7, 7,
1452 8, 8, 9, 9, 10, 10, 11, 11,
1453 12, 13, 13, 13, 13, 14, 15, 16,
1454 17, 19, 21, 25, 28, 31, 34, 37,
1455 40, 44, 48, 52, 56, 60, 64, 68,
1456 72, 80, 90, 100, 110, 120, 130, 140,
1457 150, 170, 190, 210, 230, 250, 250, 250,
1460 void CheckVehicleBreakdown(Vehicle *v)
1462 int rel, rel_old;
1464 /* decrease reliability */
1465 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1466 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1468 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1469 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1470 v->cur_speed < 5 || _game_mode == GM_MENU) {
1471 return;
1474 uint32 r = Random();
1476 /* increase chance of failure */
1477 int chance = v->breakdown_chance + 1;
1478 if (Chance16I(1, 25, r)) chance += 25;
1479 v->breakdown_chance = min(255, chance);
1481 /* calculate reliability value to use in comparison */
1482 rel = v->reliability;
1483 if (v->type == VEH_SHIP) rel += 0x6666;
1485 /* reduced breakdowns? */
1486 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1488 /* check if to break down */
1489 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1490 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1491 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1492 v->breakdown_chance = 0;
1497 * Handle all of the aspects of a vehicle breakdown
1498 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1499 * @return true iff the vehicle is stopped because of a breakdown
1500 * @note This function always returns false for aircraft, since these never stop for breakdowns
1502 bool Vehicle::HandleBreakdown()
1504 /* Possible states for Vehicle::breakdown_ctr
1505 * 0 - vehicle is running normally
1506 * 1 - vehicle is currently broken down
1507 * 2 - vehicle is going to break down now
1508 * >2 - vehicle is counting down to the actual breakdown event */
1509 switch (this->breakdown_ctr) {
1510 case 0:
1511 return false;
1513 case 2:
1514 this->breakdown_ctr = 1;
1516 if (this->breakdowns_since_last_service != 255) {
1517 this->breakdowns_since_last_service++;
1520 if (this->type == VEH_AIRCRAFT) {
1521 /* Aircraft just need this flag, the rest is handled elsewhere */
1522 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1523 } else {
1524 this->cur_speed = 0;
1526 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1527 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1528 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1529 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1530 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1533 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1534 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1535 if (u != nullptr) u->animation_state = this->breakdown_delay * 2;
1539 this->MarkDirty(); // Update graphics after speed is zeroed
1540 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1541 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1543 FALLTHROUGH;
1544 case 1:
1545 /* Aircraft breakdowns end only when arriving at the airport */
1546 if (this->type == VEH_AIRCRAFT) return false;
1548 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1549 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1550 if (--this->breakdown_delay == 0) {
1551 this->breakdown_ctr = 0;
1552 this->MarkDirty();
1553 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1556 return true;
1558 default:
1559 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1560 return false;
1565 * Update age of a vehicle.
1566 * @param v Vehicle to update.
1568 void AgeVehicle(Vehicle *v)
1570 /* Stop if a virtual vehicle */
1571 if (HasBit(v->subtype, GVSF_VIRTUAL)) return;
1573 if (v->age < MAX_DAY) {
1574 v->age++;
1575 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1578 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1580 int age = v->age - v->max_age;
1581 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1582 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1583 v->reliability_spd_dec <<= 1;
1586 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1588 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1589 if (v->Previous() != nullptr || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1591 /* Don't warn if a renew is active */
1592 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1594 StringID str;
1595 if (age == -DAYS_IN_LEAP_YEAR) {
1596 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1597 } else if (age == 0) {
1598 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1599 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1600 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1601 } else {
1602 return;
1605 SetDParam(0, v->index);
1606 AddVehicleAdviceNewsItem(str, v->index);
1610 * Calculates how full a vehicle is.
1611 * @param front The front vehicle of the consist to check.
1612 * @param colour The string to show depending on if we are unloading or loading
1613 * @return A percentage of how full the Vehicle is.
1614 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1615 * if the vehicle is completely empty or full.
1616 * This is useful for both display and conditional orders.
1618 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1620 int count = 0;
1621 int max = 0;
1622 int cars = 0;
1623 int unloading = 0;
1624 bool loading = false;
1626 bool is_loading = front->current_order.IsType(OT_LOADING);
1628 /* The station may be nullptr when the (colour) string does not need to be set. */
1629 const Station *st = Station::GetIfValid(front->last_station_visited);
1630 assert(colour == nullptr || (st != nullptr && is_loading));
1632 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1633 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1635 /* Count up max and used */
1636 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
1637 count += v->cargo.StoredCount();
1638 max += v->cargo_cap;
1639 if (v->cargo_cap != 0 && colour != nullptr) {
1640 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1641 loading |= !order_no_load &&
1642 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1643 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1644 cars++;
1648 if (colour != nullptr) {
1649 if (unloading == 0 && loading) {
1650 *colour = STR_PERCENT_UP;
1651 } else if (unloading == 0 && !loading) {
1652 *colour = STR_PERCENT_NONE;
1653 } else if (cars == unloading || !loading) {
1654 *colour = STR_PERCENT_DOWN;
1655 } else {
1656 *colour = STR_PERCENT_UP_DOWN;
1660 /* Train without capacity */
1661 if (max == 0) return 100;
1663 /* Return the percentage */
1664 if (count * 2 < max) {
1665 /* Less than 50%; round up, so that 0% means really empty. */
1666 return CeilDiv(count * 100, max);
1667 } else {
1668 /* More than 50%; round down, so that 100% means really full. */
1669 return (count * 100) / max;
1674 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1675 * @param v Vehicle that entered a depot.
1677 void VehicleEnterDepot(Vehicle *v)
1679 /* Always work with the front of the vehicle */
1680 assert(v == v->First());
1682 switch (v->type) {
1683 case VEH_TRAIN: {
1684 Train *t = Train::From(v);
1685 SetWindowClassesDirty(WC_TRAINS_LIST);
1686 SetWindowClassesDirty(WC_TRACE_RESTRICT_SLOTS);
1687 /* Clear path reservation */
1688 SetDepotReservation(t->tile, false);
1689 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile, ZOOM_LVL_DRAW_MAP);
1691 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1692 t->wait_counter = 0;
1693 t->force_proceed = TFP_NONE;
1694 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1695 t->ConsistChanged(CCF_ARRANGE);
1696 t->reverse_distance = 0;
1697 break;
1700 case VEH_ROAD:
1701 SetWindowClassesDirty(WC_ROADVEH_LIST);
1702 break;
1704 case VEH_SHIP: {
1705 SetWindowClassesDirty(WC_SHIPS_LIST);
1706 Ship *ship = Ship::From(v);
1707 ship->state = TRACK_BIT_DEPOT;
1708 ship->UpdateCache();
1709 ship->UpdateViewport(true, true);
1710 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1711 break;
1714 case VEH_AIRCRAFT:
1715 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1716 HandleAircraftEnterHangar(Aircraft::From(v));
1717 break;
1718 default: NOT_REACHED();
1720 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1722 if (v->type != VEH_TRAIN) {
1723 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1724 * 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 */
1725 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1727 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1729 v->vehstatus |= VS_HIDDEN;
1730 v->cur_speed = 0;
1732 VehicleServiceInDepot(v);
1734 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1735 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1736 v->MarkDirty();
1738 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1739 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1741 if (v->type != VEH_TRAIN && v->current_order.GetDepotOrderType() == ODTF_MANUAL) {
1742 // Check first if the vehicle has any depot in its order list. If yes then we're heading for a specific depot.
1743 // Don't stop if this one isn't it.
1744 bool has_depot_in_orders = false;
1746 for (int i = 0; i < v->GetNumOrders(); ++i) {
1747 Order* order = v->GetOrder(i);
1749 bool isRegularOrder = (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
1750 bool isDepotOrder = order->GetType() == OT_GOTO_DEPOT;
1752 if (isRegularOrder && isDepotOrder) {
1753 has_depot_in_orders = true;
1754 break;
1758 if (has_depot_in_orders)
1760 if ((v->type == VEH_AIRCRAFT && Station::GetByTile(v->dest_tile)->index != Station::GetByTile(v->tile)->index) ||
1761 (v->type != VEH_AIRCRAFT && v->dest_tile != v->tile)) {
1762 /* We are heading for another depot, keep driving. */
1763 return;
1768 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1770 /* Test whether we are heading for this depot. If not, do nothing.
1771 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1772 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1773 real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1774 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1775 /* We are heading for another depot, keep driving. */
1776 return;
1779 if (v->current_order.IsRefit()) {
1780 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1781 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1782 cur_company.Restore();
1784 if (cost.Failed()) {
1785 _vehicles_to_autoreplace[v] = false;
1786 if (v->owner == _local_company) {
1787 /* Notify the user that we stopped the vehicle */
1788 SetDParam(0, v->index);
1789 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1791 } else if (cost.GetCost() != 0) {
1792 v->profit_this_year -= cost.GetCost() << 8;
1793 if (v->owner == _local_company) {
1794 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1799 /* Handle the ODTFB_PART_OF_ORDERS case. If there is a timetabled wait time, hold the vehicle, otherwise skip to the next order.
1800 Note that if there is a only a travel_time, but no wait_time defined for the order, and the vehicle arrives to the depot sooner as scheduled,
1801 he doesn't wait in it, as it would in stations. Thus, the original behavior is maintained if there's no defined wait_time.*/
1802 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1803 v->DeleteUnreachedImplicitOrders();
1804 UpdateVehicleTimetable(v, true);
1805 if (v->current_order.IsWaitTimetabled() && !(v->current_order.GetDepotActionType() & ODATFB_HALT)) {
1806 v->current_order.MakeWaiting();
1807 v->HandleAutomaticTimetableSeparation();
1808 v->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1809 return;
1811 else {
1812 UpdateVehicleTimetable(v, false);
1813 v->IncrementImplicitOrderIndex();
1816 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1817 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1818 _vehicles_to_autoreplace[v] = false;
1819 /* Invalidate last_loading_station. As the link from the station
1820 * before the stop to the station after the stop can't be predicted
1821 * we shouldn't construct it when the vehicle visits the next stop. */
1822 v->last_loading_station = INVALID_STATION;
1823 ClrBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP);
1824 if (v->owner == _local_company) {
1825 SetDParam(0, v->index);
1826 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1828 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1829 v->MarkSeparationInvalid();
1831 v->current_order.MakeDummy();
1837 * Update the position of the vehicle. This will update the hash that tells
1838 * which vehicles are on a tile.
1840 void Vehicle::UpdatePosition()
1842 UpdateVehicleTileHash(this, false);
1846 * Update the vehicle on the viewport, updating the right hash and setting the
1847 * new coordinates.
1848 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1850 void Vehicle::UpdateViewport(bool dirty)
1852 Rect new_coord = ConvertRect<Rect16, Rect>(this->sprite_seq_bounds);
1854 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1855 new_coord.left += pt.x;
1856 new_coord.top += pt.y;
1857 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1858 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1860 UpdateVehicleViewportHash(this, new_coord.left, new_coord.top);
1862 Rect old_coord = this->coord;
1863 this->coord = new_coord;
1865 if (dirty) {
1866 if (old_coord.left == INVALID_COORD) {
1867 this->MarkAllViewportsDirty();
1868 } else {
1869 ::MarkAllViewportsDirty(
1870 min(old_coord.left, this->coord.left),
1871 min(old_coord.top, this->coord.top),
1872 max(old_coord.right, this->coord.right),
1873 max(old_coord.bottom, this->coord.bottom),
1874 this->type != VEH_EFFECT ? ZOOM_LVL_END : ZOOM_LVL_DRAW_MAP);
1880 * Update the position of the vehicle, and update the viewport.
1882 void Vehicle::UpdatePositionAndViewport()
1884 this->UpdatePosition();
1885 this->UpdateViewport(true);
1889 * Marks viewports dirty where the vehicle's image is.
1891 void Vehicle::MarkAllViewportsDirty() const
1893 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom, this->type != VEH_EFFECT ? ZOOM_LVL_END : ZOOM_LVL_DRAW_MAP);
1897 * Get position information of a vehicle when moving one pixel in the direction it is facing
1898 * @param v Vehicle to move
1899 * @return Position information after the move
1901 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1903 static const int8 _delta_coord[16] = {
1904 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1905 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1908 int x = v->x_pos + _delta_coord[v->direction];
1909 int y = v->y_pos + _delta_coord[v->direction + 8];
1911 GetNewVehiclePosResult gp;
1912 gp.x = x;
1913 gp.y = y;
1914 gp.old_tile = v->tile;
1915 gp.new_tile = TileVirtXY(x, y);
1916 return gp;
1919 static const Direction _new_direction_table[] = {
1920 DIR_N, DIR_NW, DIR_W,
1921 DIR_NE, DIR_SE, DIR_SW,
1922 DIR_E, DIR_SE, DIR_S
1925 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1927 int i = 0;
1929 if (y >= v->y_pos) {
1930 if (y != v->y_pos) i += 3;
1931 i += 3;
1934 if (x >= v->x_pos) {
1935 if (x != v->x_pos) i++;
1936 i++;
1939 Direction dir = v->direction;
1941 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1942 if (dirdiff == DIRDIFF_SAME) return dir;
1943 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1947 * Call the tile callback function for a vehicle entering a tile
1948 * @param v Vehicle entering the tile
1949 * @param tile Tile entered
1950 * @param x X position
1951 * @param y Y position
1952 * @return Some meta-data over the to be entered tile.
1953 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1955 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1957 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1961 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1962 * struct initialization, except after each call to this->NextID() the returned value
1963 * is assigned to a vehicle.
1964 * @param type type of vehicle
1965 * @param owner owner of vehicles
1967 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(nullptr), maxid(0), curid(0)
1969 /* Find maximum */
1970 const Vehicle *v;
1971 FOR_ALL_VEHICLES(v) {
1972 if (v->type == type && v->owner == owner) {
1973 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1977 if (this->maxid == 0) return;
1979 /* Reserving 'maxid + 2' because we need:
1980 * - space for the last item (with v->unitnumber == maxid)
1981 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1982 this->cache = CallocT<bool>(this->maxid + 2);
1984 /* Fill the cache */
1985 FOR_ALL_VEHICLES(v) {
1986 if (v->type == type && v->owner == owner) {
1987 this->cache[v->unitnumber] = true;
1992 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1993 UnitID FreeUnitIDGenerator::NextID()
1995 if (this->maxid <= this->curid) return ++this->curid;
1997 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1999 return this->curid;
2003 * Get an unused unit number for a vehicle (if allowed).
2004 * @param type Type of vehicle
2005 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
2007 UnitID GetFreeUnitNumber(VehicleType type)
2009 /* Check whether it is allowed to build another vehicle. */
2010 uint max_veh;
2011 switch (type) {
2012 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
2013 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
2014 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
2015 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
2016 default: NOT_REACHED();
2019 const Company *c = Company::Get(_current_company);
2020 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
2022 FreeUnitIDGenerator gen(type, _current_company);
2024 return gen.NextID();
2029 * Check whether we can build infrastructure for the given
2030 * vehicle type. This to disable building stations etc. when
2031 * you are not allowed/able to have the vehicle type yet.
2032 * @param type the vehicle type to check this for
2033 * @return true if there is any reason why you may build
2034 * the infrastructure for the given vehicle type
2036 bool CanBuildVehicleInfrastructure(VehicleType type)
2038 assert(IsCompanyBuildableVehicleType(type));
2040 if (!Company::IsValidID(_local_company)) return false;
2041 if (!_settings_client.gui.disable_unsuitable_building) return true;
2043 UnitID max;
2044 switch (type) {
2045 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
2046 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
2047 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
2048 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
2049 default: NOT_REACHED();
2052 /* We can build vehicle infrastructure when we may build the vehicle type */
2053 if (max > 0) {
2054 /* Can we actually build the vehicle type? */
2055 const Engine *e;
2056 FOR_ALL_ENGINES_OF_TYPE(e, type) {
2057 if (HasBit(e->company_avail, _local_company)) return true;
2059 return false;
2062 /* We should be able to build infrastructure when we have the actual vehicle type */
2063 const Vehicle *v;
2064 FOR_ALL_VEHICLES(v) {
2065 if (v->owner == _local_company && v->type == type) return true;
2068 return false;
2073 * Determines the #LiveryScheme for a vehicle.
2074 * @param engine_type Engine of the vehicle.
2075 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
2076 * @param v the vehicle, \c nullptr if in purchase list etc.
2077 * @return livery scheme to use.
2079 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
2081 CargoID cargo_type = v == nullptr ? (CargoID)CT_INVALID : v->cargo_type;
2082 const Engine *e = Engine::Get(engine_type);
2083 switch (e->type) {
2084 default: NOT_REACHED();
2085 case VEH_TRAIN:
2086 if (v != nullptr && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
2087 /* Wagonoverrides use the colour scheme of the front engine.
2088 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
2089 engine_type = parent_engine_type;
2090 e = Engine::Get(engine_type);
2091 /* Note: Luckily cargo_type is not needed for engines */
2094 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
2095 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
2096 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
2097 if (!CargoSpec::Get(cargo_type)->is_freight) {
2098 if (parent_engine_type == INVALID_ENGINE) {
2099 return LS_PASSENGER_WAGON_STEAM;
2100 } else {
2101 switch (RailVehInfo(parent_engine_type)->engclass) {
2102 default: NOT_REACHED();
2103 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
2104 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
2105 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
2106 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
2107 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
2110 } else {
2111 return LS_FREIGHT_WAGON;
2113 } else {
2114 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
2116 switch (e->u.rail.engclass) {
2117 default: NOT_REACHED();
2118 case EC_STEAM: return LS_STEAM;
2119 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
2120 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
2121 case EC_MONORAIL: return LS_MONORAIL;
2122 case EC_MAGLEV: return LS_MAGLEV;
2126 case VEH_ROAD:
2127 /* Always use the livery of the front */
2128 if (v != nullptr && parent_engine_type != INVALID_ENGINE) {
2129 engine_type = parent_engine_type;
2130 e = Engine::Get(engine_type);
2131 cargo_type = v->First()->cargo_type;
2133 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
2134 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
2136 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
2137 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
2138 /* Tram */
2139 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
2140 } else {
2141 /* Bus or truck */
2142 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
2145 case VEH_SHIP:
2146 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
2147 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
2148 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
2150 case VEH_AIRCRAFT:
2151 switch (e->u.air.subtype) {
2152 case AIR_HELI: return LS_HELICOPTER;
2153 case AIR_CTOL: return LS_SMALL_PLANE;
2154 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
2155 default: NOT_REACHED();
2161 * Determines the livery for a vehicle.
2162 * @param engine_type EngineID of the vehicle
2163 * @param company Owner of the vehicle
2164 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
2165 * @param v the vehicle. nullptr if in purchase list etc.
2166 * @param livery_setting The livery settings to use for acquiring the livery information.
2167 * @return livery to use
2169 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
2171 const Company *c = Company::Get(company);
2172 LiveryScheme scheme = LS_DEFAULT;
2174 /* The default livery is always available for use, but its in_use flag determines
2175 * whether any _other_ liveries are in use. */
2176 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
2177 /* Determine the livery scheme to use */
2178 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
2180 /* Switch back to the default scheme if the resolved scheme is not in use */
2181 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
2184 return &c->livery[scheme];
2188 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
2190 PaletteID map = (v != nullptr) ? v->colourmap : PAL_NONE;
2192 /* Return cached value if any */
2193 if (map != PAL_NONE) return map;
2195 const Engine *e = Engine::Get(engine_type);
2197 /* Check if we should use the colour map callback */
2198 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
2199 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
2200 /* Failure means "use the default two-colour" */
2201 if (callback != CALLBACK_FAILED) {
2202 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2203 map = GB(callback, 0, 14);
2204 /* If bit 14 is set, then the company colours are applied to the
2205 * map else it's returned as-is. */
2206 if (!HasBit(callback, 14)) {
2207 /* Update cache */
2208 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2209 return map;
2214 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
2216 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
2218 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2219 if (!Company::IsValidID(company)) return map;
2221 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2223 map += livery->colour1;
2224 if (twocc) map += livery->colour2 * 16;
2226 /* Update cache */
2227 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2228 return map;
2232 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2233 * @param engine_type ID of engine
2234 * @param company ID of company
2235 * @return A ready-to-use palette modifier
2237 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2239 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, nullptr);
2243 * Get the colour map for a vehicle.
2244 * @param v Vehicle to get colour map for
2245 * @return A ready-to-use palette modifier
2247 PaletteID GetVehiclePalette(const Vehicle *v)
2249 if (v->IsGroundVehicle()) {
2250 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
2253 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
2257 * Delete all implicit orders which were not reached.
2259 void Vehicle::DeleteUnreachedImplicitOrders()
2261 if (this->IsGroundVehicle()) {
2262 uint16 &gv_flags = this->GetGroundVehicleFlags();
2263 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2264 /* Do not delete orders, only skip them */
2265 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2266 this->cur_implicit_order_index = this->cur_real_order_index;
2267 InvalidateVehicleOrder(this, 0);
2268 return;
2272 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2273 while (order != nullptr) {
2274 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2276 if (order->IsType(OT_IMPLICIT)) {
2277 DeleteOrder(this, this->cur_implicit_order_index);
2278 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2279 order = this->GetOrder(this->cur_implicit_order_index);
2280 } else {
2281 /* Skip non-implicit orders, e.g. service-orders */
2282 order = order->next;
2283 this->cur_implicit_order_index++;
2286 /* Wrap around */
2287 if (order == nullptr) {
2288 order = this->GetOrder(0);
2289 this->cur_implicit_order_index = 0;
2295 * Increase capacity for all link stats associated with vehicles in the given consist.
2296 * @param st Station to get the link stats from.
2297 * @param front First vehicle in the consist.
2298 * @param next_station_id Station the consist will be travelling to next.
2300 static void VehicleIncreaseStats(const Vehicle *front)
2302 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
2303 StationID last_loading_station = HasBit(front->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? v->last_loading_station : front->last_loading_station;
2304 if (v->refit_cap > 0 &&
2305 last_loading_station != INVALID_STATION &&
2306 last_loading_station != front->last_station_visited &&
2307 ((front->current_order.GetCargoLoadType(v->cargo_type) & OLFB_NO_LOAD) == 0 ||
2308 (front->current_order.GetCargoUnloadType(v->cargo_type) & OUFB_NO_UNLOAD) == 0)) {
2309 /* The cargo count can indeed be higher than the refit_cap if
2310 * wagons have been auto-replaced and subsequently auto-
2311 * refitted to a higher capacity. The cargo gets redistributed
2312 * among the wagons in that case.
2313 * As usage is not such an important figure anyway we just
2314 * ignore the additional cargo then.*/
2315 IncreaseStats(Station::Get(last_loading_station), v->cargo_type, front->last_station_visited, v->refit_cap,
2316 min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
2322 * Prepare everything to begin the loading when arriving at a station.
2323 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2325 void Vehicle::BeginLoading()
2327 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2329 if (this->current_order.IsType(OT_GOTO_STATION) &&
2330 this->current_order.GetDestination() == this->last_station_visited) {
2331 this->DeleteUnreachedImplicitOrders();
2333 /* Now both order indices point to the destination station, and we can start loading */
2334 this->current_order.MakeLoading(true);
2335 UpdateVehicleTimetable(this, true);
2337 /* Furthermore add the Non Stop flag to mark that this station
2338 * is the actual destination of the vehicle, which is (for example)
2339 * necessary to be known for HandleTrainLoading to determine
2340 * whether the train is lost or not; not marking a train lost
2341 * that arrives at random stations is bad. */
2342 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2344 } else {
2345 /* We weren't scheduled to stop here. Insert an implicit order
2346 * to show that we are stopping here.
2347 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2348 * the 'wrong' terminal when skipping orders etc. */
2349 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2350 if (this->IsGroundVehicle() &&
2351 (in_list == nullptr || !in_list->IsType(OT_IMPLICIT) ||
2352 in_list->GetDestination() != this->last_station_visited)) {
2353 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2354 /* Do not create consecutive duplicates of implicit orders */
2355 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2356 if (prev_order == nullptr ||
2357 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2358 prev_order->GetDestination() != this->last_station_visited) {
2360 /* Prefer deleting implicit orders instead of inserting new ones,
2361 * so test whether the right order follows later. In case of only
2362 * implicit orders treat the last order in the list like an
2363 * explicit one, except if the overall number of orders surpasses
2364 * IMPLICIT_ORDER_ONLY_CAP. */
2365 int target_index = this->cur_implicit_order_index;
2366 bool found = false;
2367 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2368 const Order *order = this->GetOrder(target_index);
2369 if (order == nullptr) break; // No orders.
2370 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2371 found = true;
2372 break;
2374 target_index++;
2375 if (target_index >= this->GetNumOrders()) {
2376 if (this->GetNumManualOrders() == 0 &&
2377 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2378 break;
2380 target_index = 0;
2382 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2385 if (found) {
2386 if (suppress_implicit_orders) {
2387 /* Skip to the found order */
2388 this->cur_implicit_order_index = target_index;
2389 InvalidateVehicleOrder(this, 0);
2390 } else {
2391 /* Delete all implicit orders up to the station we just reached */
2392 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2393 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2394 if (order->IsType(OT_IMPLICIT)) {
2395 DeleteOrder(this, this->cur_implicit_order_index);
2396 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2397 order = this->GetOrder(this->cur_implicit_order_index);
2398 } else {
2399 /* Skip non-implicit orders, e.g. service-orders */
2400 order = order->next;
2401 this->cur_implicit_order_index++;
2404 /* Wrap around */
2405 if (order == nullptr) {
2406 order = this->GetOrder(0);
2407 this->cur_implicit_order_index = 0;
2409 assert(order != nullptr);
2412 } else if (!suppress_implicit_orders &&
2413 (this->HasOrdersList() ? this->GetNumOrders() < MAX_VEH_ORDER_ID : OrderList::CanAllocateItem()) &&
2414 Order::CanAllocateItem()) {
2415 /* Insert new implicit order */
2416 Order *implicit_order = new Order();
2417 implicit_order->MakeImplicit(this->last_station_visited);
2418 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2419 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2421 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2422 * Reenable it for this vehicle */
2423 uint16 &gv_flags = this->GetGroundVehicleFlags();
2424 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2429 this->current_order.MakeLoading(false);
2432 this->HandleAutomaticTimetableSeparation();
2434 VehicleIncreaseStats(this);
2436 PrepareUnload(this);
2438 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2439 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2440 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2441 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2443 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2444 this->cur_speed = 0;
2445 this->MarkDirty();
2449 * Handles the automatic timetable separation from initialization to setting of the lateness counter at the correct first order.
2451 void Vehicle::HandleAutomaticTimetableSeparation()
2453 /* If all requirements for separation are met, we can initialize it. */
2454 if (!_settings_game.order.automatic_timetable_separation) return;
2455 if (!this->HasSharedOrdersList()) return;
2456 assert(this->orders.list != nullptr);
2457 if (!this->orders.list->IsCompleteTimetable()) return;
2459 int first_wait_index = -1;
2461 for (int i = 0; i < this->GetNumOrders(); ++i) {
2462 Order* order = this->orders.list->GetOrderAt(i);
2464 if (order->IsWaitTimetabled() && !order->IsType(OT_IMPLICIT)) {
2465 first_wait_index = i;
2466 break;
2470 if (this->cur_implicit_order_index != first_wait_index) return;
2472 if (!this->orders.list->IsSeparationValid()) {
2473 this->orders.list->InitializeSeparation();
2474 this->lateness_counter = this->orders.list->SeparateVehicle();
2475 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
2477 else
2479 this->lateness_counter = this->orders.list->SeparateVehicle();
2481 if (this->lateness_counter > 0)
2483 this->orders.list->InitializeSeparation();
2484 this->lateness_counter = this->orders.list->SeparateVehicle();
2485 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
2489 SetBit(this->vehicle_flags, VF_SEPARATION_IN_PROGRESS);
2493 * Return all reserved cargo packets to the station and reset all packets
2494 * staged for transfer.
2495 * @param st the station where the reserved packets should go.
2497 void Vehicle::CancelReservation(StationID next, Station *st)
2499 for (Vehicle *v = this; v != nullptr; v = v->next) {
2500 VehicleCargoList &cargo = v->cargo;
2501 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2502 DEBUG(misc, 1, "cancelling cargo reservation");
2503 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2504 cargo.SetTransferLoadPlace(st->xy);
2506 cargo.KeepAll();
2510 uint32 Vehicle::GetLastLoadingStationValidCargoMask() const
2512 if (!HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP)) {
2513 return (this->last_loading_station != INVALID_STATION) ? ~0 : 0;
2514 } else {
2515 uint32 cargo_mask = 0;
2516 for (const Vehicle *u = this; u != nullptr; u = u->Next()) {
2517 if (u->cargo_type < NUM_CARGO && u->last_loading_station != INVALID_STATION) {
2518 SetBit(cargo_mask, u->cargo_type);
2521 return cargo_mask;
2526 * Perform all actions when leaving a station.
2527 * @pre this->current_order.IsType(OT_LOADING)
2529 void Vehicle::LeaveStation()
2531 assert(this->current_order.IsType(OT_LOADING));
2533 delete this->cargo_payment;
2534 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
2536 /* Only update the timetable if the vehicle was supposed to stop here. */
2537 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2539 uint32 cargoes_can_load_unload = this->current_order.FilterLoadUnloadTypeCargoMask([&](const Order *o, CargoID cargo) {
2540 return ((o->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0) || ((o->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0);
2542 uint32 has_cargo_mask = this->GetLastLoadingStationValidCargoMask();
2543 uint32 cargoes_can_leave_with_cargo = FilterCargoMask([&](CargoID cargo) {
2544 return this->current_order.CanLeaveWithCargo(HasBit(has_cargo_mask, cargo), cargo);
2545 }, cargoes_can_load_unload);
2547 if (cargoes_can_load_unload != 0) {
2548 if (cargoes_can_leave_with_cargo != 0) {
2549 /* Refresh next hop stats to make sure we've done that at least once
2550 * during the stop and that refit_cap == cargo_cap for each vehicle in
2551 * the consist. */
2552 this->ResetRefitCaps();
2553 LinkRefresher::Run(this, true, false, cargoes_can_leave_with_cargo);
2556 if (cargoes_can_leave_with_cargo == (uint32) ~0) {
2557 /* can leave with all cargoes */
2559 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2560 this->last_loading_station = this->last_station_visited;
2561 ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2562 } else if (cargoes_can_leave_with_cargo == 0) {
2563 /* can leave with no cargoes */
2565 /* if the vehicle couldn't load and had to unload or transfer everything
2566 * set the last loading station to invalid as it will leave empty. */
2567 this->last_loading_station = INVALID_STATION;
2568 ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2569 } else {
2570 /* mix of cargoes loadable or could not leave with all cargoes */
2572 /* NB: this is saved here as we overwrite it on the first iteration of the loop below */
2573 StationID head_last_loading_station = this->last_loading_station;
2574 for (Vehicle *u = this; u != nullptr; u = u->Next()) {
2575 StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station;
2576 if (u->cargo_type < NUM_CARGO && HasBit(cargoes_can_load_unload, u->cargo_type)) {
2577 if (HasBit(cargoes_can_leave_with_cargo, u->cargo_type)) {
2578 u->last_loading_station = this->last_station_visited;
2579 } else {
2580 u->last_loading_station = INVALID_STATION;
2582 } else {
2583 u->last_loading_station = last_loading_station;
2586 SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2591 int last_loading_order_index = -1;
2593 // Reverse iterate through the orders list and find the first (i.e. last) order that is of loading type.
2594 for (int i = this->GetNumOrders() - 1; i >= 0; --i) {
2595 Order* order = this->orders.list->GetOrderAt(i);
2597 bool can_load_or_unload = false;
2599 if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_IMPLICIT)) &&
2600 (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
2601 if (order->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || order->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
2602 can_load_or_unload = true;
2603 } else if ((order->GetLoadType() & OLFB_NO_LOAD) == 0 || (order->GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2604 can_load_or_unload = true;
2608 if (can_load_or_unload && !(order->GetLoadType() & OLFB_NO_LOAD)) {
2609 last_loading_order_index = i;
2610 break;
2614 if (last_loading_order_index >= 0 && last_loading_order_index < this->GetNumOrders()) {
2616 Order* current_real_order = this->orders.list->GetOrderAt(this->cur_real_order_index);
2618 bool can_load_or_unload = false;
2620 if ((current_real_order->IsType(OT_GOTO_STATION) || current_real_order->IsType(OT_IMPLICIT)) &&
2621 (current_real_order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
2622 if (current_real_order->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || current_real_order->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
2623 can_load_or_unload = true;
2625 else if ((current_real_order->GetLoadType() & OLFB_NO_LOAD) == 0 || (current_real_order->GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2626 can_load_or_unload = true;
2630 bool current_order_was_load_order = (can_load_or_unload && !(current_real_order->GetLoadType() & OLFB_NO_LOAD));
2632 if (current_order_was_load_order)
2634 int8 occupancy = (int8)CalcPercentVehicleFilled(this, nullptr);
2635 station_occupancies.push_back(occupancy);
2638 bool wasLastLoadingOrder = this->cur_real_order_index == last_loading_order_index;
2640 if (wasLastLoadingOrder)
2642 if (station_occupancies.size() != 0)
2644 int sum = 0;
2646 std::vector<int8>::const_iterator it;
2647 for (it = station_occupancies.begin(); it != station_occupancies.end(); ++it)
2648 sum += *it;
2650 station_occupancies.clear();
2654 else {
2655 station_occupancies.clear();
2658 this->current_order.MakeLeaveStation();
2659 Station *st = Station::Get(this->last_station_visited);
2660 this->CancelReservation(INVALID_STATION, st);
2661 st->loading_vehicles.erase(std::remove(st->loading_vehicles.begin(), st->loading_vehicles.end(), this), st->loading_vehicles.end());
2663 HideFillingPercent(&this->fill_percent_te_id);
2665 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2666 /* Trigger station animation (trains only) */
2667 if (IsTileType(this->tile, MP_STATION)) {
2668 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2669 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2672 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2675 this->MarkDirty();
2679 * Reset all refit_cap in the consist to cargo_cap.
2681 void Vehicle::ResetRefitCaps()
2683 for (Vehicle *v = this; v != nullptr; v = v->Next()) v->refit_cap = v->cargo_cap;
2687 * Handle the loading of the vehicle; when not it skips through dummy
2688 * orders and does nothing in all other cases.
2689 * @param mode is the non-first call for this vehicle in this tick?
2691 void Vehicle::HandleLoading(bool mode)
2693 OrderType current_order_type = this->current_order.GetType();
2695 switch (current_order_type) {
2696 case OT_LOADING: {
2697 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2699 /* Save time just loading took since that is what goes into the timetable */
2700 if (!HasBit(this->vehicle_flags, VF_LOADING_FINISHED)) {
2701 this->current_loading_time = this->current_order_time;
2704 bool has_manual_depot_order = (HasBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT) || HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT));
2706 if (!has_manual_depot_order) {
2707 /* Not the first call for this tick, or still loading */
2708 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2711 this->PlayLeaveStationSound();
2713 this->LeaveStation();
2715 /* Only advance to next order if we just loaded at the current one */
2716 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2717 if (order == nullptr ||
2718 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2719 order->GetDestination() != this->last_station_visited) {
2720 return;
2722 break;
2725 case OT_DUMMY: break;
2727 default: return;
2730 this->IncrementImplicitOrderIndex();
2734 * Handle the waiting time everywhere else as in stations (basically in depot but, eventually, also elsewhere ?)
2735 * Function is called when order's wait_time is defined.
2736 * @param stop_waiting should we stop waiting (or definitely avoid) even if there is still time left to wait ?
2738 void Vehicle::HandleWaiting(bool stop_waiting)
2740 switch (this->current_order.GetType()) {
2741 case OT_WAITING: {
2742 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2743 /* Vehicles holds on until waiting Timetabled time expires. */
2744 if (!stop_waiting && this->current_order_time < wait_time) {
2745 return;
2748 /* When wait_time is expired, we move on. */
2749 UpdateVehicleTimetable(this, false);
2750 this->IncrementImplicitOrderIndex();
2751 this->current_order.MakeDummy();
2753 break;
2756 default: return;
2761 * Send this vehicle to the depot using the given command(s).
2762 * @param flags the command flags (like execute and such).
2763 * @param command the command to execute.
2764 * @return the cost of the depot action.
2766 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2768 CommandCost ret = CheckOwnership(this->owner);
2769 if (ret.Failed()) return ret;
2771 if (this->vehstatus & VS_CRASHED) return CommandError();
2772 if (this->IsStoppedInDepot()) return CommandError();
2774 if (HasBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT) || HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT)) {
2775 bool service_only = (command & DEPOT_SERVICE) != 0;
2776 if (service_only != HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT)) {
2777 /* We called with a different DEPOT_SERVICE setting.
2778 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2779 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2780 if (flags & DC_EXEC) {
2781 ClrBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
2782 ClrBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
2783 SetBit(this->vehicle_flags, service_only ? VF_SHOULD_SERVICE_AT_DEPOT : VF_SHOULD_GOTO_DEPOT);
2784 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2786 return CommandCost();
2789 if (command & DEPOT_DONT_CANCEL) return CommandError(); // Requested no cancellation of depot orders
2791 if(flags & DC_EXEC) {
2792 ClrBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
2793 ClrBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
2794 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2796 // Prevent any attempt to update timetable for current order, as actual travel time
2797 // will be incorrect due to depot command.
2798 this->cur_timetable_order_index = INVALID_VEH_ORDER_ID;
2800 return CommandCost();
2802 } else if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() == ODTF_MANUAL) {
2803 bool current_service_only = !((this->current_order.GetDepotActionType() & ODATFB_HALT) != 0);
2804 bool service_only = (command & DEPOT_SERVICE) != 0;
2805 if (current_service_only != service_only) {
2806 /* We called with a different DEPOT_SERVICE setting.
2807 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2808 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2809 if (flags & DC_EXEC) {
2810 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2811 this->current_order.SetDepotActionType(service_only ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2812 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2814 return CommandCost();
2817 if (command & DEPOT_DONT_CANCEL) return CommandError(); // Requested no cancellation of depot orders
2818 if (flags & DC_EXEC) {
2819 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2820 * then skip to the next order; effectively canceling this forced service */
2821 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2823 if (this->IsGroundVehicle()) {
2824 uint16 &gv_flags = this->GetGroundVehicleFlags();
2825 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2828 this->current_order.MakeDummy();
2829 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2831 // Prevent any attempt to update timetable for current order, as actual travel time
2832 // will be incorrect due to depot command.
2833 this->cur_timetable_order_index = INVALID_VEH_ORDER_ID;
2836 return CommandCost();
2839 TileIndex location;
2840 DestinationID destination;
2841 bool reverse;
2842 bool foundDepotInOrders = false;
2843 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};
2845 // Check first if the vehicle has any depot in its order list. Prefer that over the closest one.
2847 for (int i = 0; i < this->GetNumOrders(); ++i) {
2848 Order* order = this->orders.list->GetOrderAt(i);
2850 bool isRegularOrder = (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
2851 bool isDepotOrder = order->GetType() == OT_GOTO_DEPOT;
2853 if (isRegularOrder && isDepotOrder) {
2854 destination = order->GetDestination();
2855 if (this->type == VEH_AIRCRAFT) {
2856 Station* st = Station::Get(destination);
2857 if (st != nullptr && st->airport.HasHangar() && CanVehicleUseStation(this, st)) {
2858 location = st->xy;
2859 foundDepotInOrders = true;
2860 break;
2863 else {
2864 location = Depot::Get(destination)->xy;
2865 reverse = false;
2866 foundDepotInOrders = true;
2867 break;
2872 if (!foundDepotInOrders && !this->FindClosestDepot(&location, &destination, &reverse)) return CommandError(no_depot[this->type]);
2874 if (flags & DC_EXEC) {
2875 if (!(foundDepotInOrders && this->type == VEH_TRAIN)) {
2876 // The OT_LOADING status of trains with depots in their order list will be handled separately in the HandleLoading() method.
2877 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2880 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2881 uint16 &gv_flags = this->GetGroundVehicleFlags();
2882 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2885 if (foundDepotInOrders && this->type == VEH_TRAIN) {
2886 bool service_only = (command & DEPOT_SERVICE) != 0;
2887 SetBit(this->vehicle_flags, service_only ? VF_SHOULD_SERVICE_AT_DEPOT : VF_SHOULD_GOTO_DEPOT);
2888 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2889 } else {
2890 this->dest_tile = location;
2891 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2893 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2894 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2896 /* If there is no depot in front, reverse automatically (trains only) */
2897 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2899 if (this->type == VEH_AIRCRAFT) {
2900 Aircraft *a = Aircraft::From(this);
2901 if (a->state == FLYING && a->targetairport != destination) {
2902 /* The aircraft is now heading for a different hangar than the next in the orders */
2903 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2904 AircraftNextAirportPos_and_Order(a);
2910 return CommandCost();
2915 * Update the cached visual effect.
2916 * @param allow_power_change true if the wagon-is-powered-state may change.
2918 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2920 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2921 const Engine *e = this->GetEngine();
2923 /* Evaluate properties */
2924 byte visual_effect;
2925 switch (e->type) {
2926 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2927 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2928 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2929 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2932 /* Check powered wagon / visual effect callback */
2933 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT) && !HasBit(this->subtype, GVSF_VIRTUAL)) {
2934 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2936 if (callback != CALLBACK_FAILED) {
2937 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2939 callback = GB(callback, 0, 8);
2940 /* Avoid accidentally setting 'visual_effect' to the default value
2941 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2942 if (callback == VE_DEFAULT) {
2943 assert(HasBit(callback, VE_DISABLE_EFFECT));
2944 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2946 visual_effect = callback;
2950 /* Apply default values */
2951 if (visual_effect == VE_DEFAULT ||
2952 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2953 /* Only train engines have default effects.
2954 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2955 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2956 if (visual_effect == VE_DEFAULT) {
2957 visual_effect = 1 << VE_DISABLE_EFFECT;
2958 } else {
2959 SetBit(visual_effect, VE_DISABLE_EFFECT);
2961 } else {
2962 if (visual_effect == VE_DEFAULT) {
2963 /* Also set the offset */
2964 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2966 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2970 this->vcache.cached_vis_effect = visual_effect;
2972 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2973 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2974 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2978 static const int8 _vehicle_smoke_pos[8] = {
2979 1, 1, 1, 0, -1, -1, -1, 0
2983 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2984 * @param v Vehicle to create effects for.
2986 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2988 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2989 if (callback == CALLBACK_FAILED) return;
2991 uint count = GB(callback, 0, 2);
2992 bool auto_center = HasBit(callback, 13);
2993 bool auto_rotate = !HasBit(callback, 14);
2995 int8 l_center = 0;
2996 if (auto_center) {
2997 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2998 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2999 } else {
3000 /* For trains: Compute offset from vehicle position to sprite position */
3001 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
3004 Direction l_dir = v->direction;
3005 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
3006 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
3008 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
3009 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
3011 for (uint i = 0; i < count; i++) {
3012 uint32 reg = GetRegister(0x100 + i);
3013 uint type = GB(reg, 0, 8);
3014 int8 x = GB(reg, 8, 8);
3015 int8 y = GB(reg, 16, 8);
3016 int8 z = GB(reg, 24, 8);
3018 if (auto_rotate) {
3019 int8 l = x;
3020 int8 t = y;
3021 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
3022 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
3025 if (type >= 0xF0) {
3026 switch (type) {
3027 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
3028 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
3029 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
3030 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
3031 default: break;
3037 uint16 ReversingDistanceTargetSpeed(const Train *v);
3040 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
3041 * @pre this->IsPrimaryVehicle()
3043 void Vehicle::ShowVisualEffect() const
3045 assert(this->IsPrimaryVehicle());
3046 bool sound = false;
3048 /* Do not show any smoke when:
3049 * - vehicle smoke is disabled by the player
3050 * - the vehicle is slowing down or stopped (by the player)
3051 * - the vehicle is moving very slowly
3053 if (_settings_game.vehicle.smoke_amount == 0 ||
3054 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
3055 this->cur_speed < 2) {
3056 return;
3059 /* Use the speed as limited by underground and orders. */
3060 uint max_speed = this->GetCurrentMaxSpeed();
3062 if (this->type == VEH_TRAIN) {
3063 const Train *t = Train::From(this);
3064 /* For trains, do not show any smoke when:
3065 * - the train is reversing
3066 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
3067 * - is approaching a reversing point and its speed is equal to maximum approach speed
3069 if (HasBit(t->flags, VRF_REVERSING) ||
3070 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
3071 t->cur_speed >= max_speed) ||
3072 (t->reverse_distance >= 1 && t->cur_speed >= ReversingDistanceTargetSpeed(t))) {
3073 return;
3077 const Vehicle *v = this;
3079 do {
3080 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
3081 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
3082 VisualEffectSpawnModel effect_model = VESM_NONE;
3083 if (advanced) {
3084 effect_offset = VE_OFFSET_CENTRE;
3085 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
3086 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
3087 } else {
3088 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
3089 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
3090 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
3091 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
3092 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
3095 /* Show no smoke when:
3096 * - Smoke has been disabled for this vehicle
3097 * - The vehicle is not visible
3098 * - The vehicle is under a bridge
3099 * - The vehicle is on a depot tile
3100 * - The vehicle is on a tunnel tile
3101 * - The vehicle is a train engine that is currently unpowered */
3102 if (effect_model == VESM_NONE ||
3103 v->vehstatus & VS_HIDDEN ||
3104 IsBridgeAbove(v->tile) ||
3105 IsDepotTile(v->tile) ||
3106 IsTunnelTile(v->tile) ||
3107 (v->type == VEH_TRAIN &&
3108 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
3109 continue;
3112 EffectVehicleType evt = EV_END;
3113 switch (effect_model) {
3114 case VESM_STEAM:
3115 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
3116 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
3117 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
3118 * REGULATION:
3119 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
3120 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
3121 evt = EV_STEAM_SMOKE;
3123 break;
3125 case VESM_DIESEL: {
3126 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
3127 * when smoke emission stops.
3128 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
3129 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
3130 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
3131 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
3132 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
3133 * maximum speed no diesel_smoke is emitted.
3134 * REGULATION:
3135 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
3136 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
3137 int power_weight_effect = 0;
3138 if (v->type == VEH_TRAIN) {
3139 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
3141 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
3142 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
3143 evt = EV_DIESEL_SMOKE;
3145 break;
3148 case VESM_ELECTRIC:
3149 /* Electric train's spark - more often occurs when train is departing (more load)
3150 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
3151 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
3152 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
3153 * REGULATION:
3154 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
3155 if (GB(v->tick_counter, 0, 2) == 0 &&
3156 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
3157 evt = EV_ELECTRIC_SPARK;
3159 break;
3161 default:
3162 NOT_REACHED();
3165 if (evt != EV_END && advanced) {
3166 sound = true;
3167 SpawnAdvancedVisualEffect(v);
3168 } else if (evt != EV_END) {
3169 sound = true;
3171 /* The effect offset is relative to a point 4 units behind the vehicle's
3172 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
3173 * correction factor. */
3174 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
3176 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
3177 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
3179 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
3180 x = -x;
3181 y = -y;
3184 CreateEffectVehicleRel(v, x, y, 10, evt);
3186 } while ((v = v->Next()) != nullptr);
3188 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
3192 * Set the next vehicle of this vehicle.
3193 * @param next the next vehicle. nullptr removes the next vehicle.
3195 void Vehicle::SetNext(Vehicle *next)
3197 assert(this != next);
3199 if (this->next != nullptr) {
3200 /* We had an old next vehicle. Update the first and previous pointers */
3201 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
3202 v->first = this->next;
3204 this->next->previous = nullptr;
3207 this->next = next;
3209 if (this->next != nullptr) {
3210 /* A new next vehicle. Update the first and previous pointers */
3211 if (this->next->previous != nullptr) this->next->previous->next = nullptr;
3212 this->next->previous = this;
3213 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
3214 v->first = this->first;
3220 * Adds this vehicle to a shared vehicle chain.
3221 * @param shared_chain a vehicle of the chain with shared vehicles.
3222 * @pre !this->IsOrderListShared()
3224 void Vehicle::AddToShared(Vehicle *shared_chain)
3226 assert(this->previous_shared == nullptr && this->next_shared == nullptr);
3228 if (shared_chain->orders.list == nullptr) {
3229 assert(shared_chain->previous_shared == nullptr);
3230 assert(shared_chain->next_shared == nullptr);
3231 this->orders.list = shared_chain->orders.list = new OrderList(nullptr, shared_chain);
3234 this->next_shared = shared_chain->next_shared;
3235 this->previous_shared = shared_chain;
3237 shared_chain->next_shared = this;
3239 if (this->next_shared != nullptr) this->next_shared->previous_shared = this;
3241 shared_chain->orders.list->AddVehicle(this);
3242 shared_chain->orders.list->MarkSeparationInvalid();
3246 * Removes the vehicle from the shared order list.
3248 void Vehicle::RemoveFromShared()
3250 /* Remember if we were first and the old window number before RemoveVehicle()
3251 * as this changes first if needed. */
3252 bool were_first = (this->FirstShared() == this);
3253 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
3255 this->orders.list->MarkSeparationInvalid();
3256 this->orders.list->RemoveVehicle(this);
3258 if (!were_first) {
3259 /* We are not the first shared one, so only relink our previous one. */
3260 this->previous_shared->next_shared = this->NextShared();
3263 if (this->next_shared != nullptr) this->next_shared->previous_shared = this->previous_shared;
3266 if (this->orders.list->GetNumVehicles() == 1) {
3267 /* When there is only one vehicle, remove the shared order list window. */
3268 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
3269 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
3270 } else if (were_first) {
3271 /* If we were the first one, update to the new first one.
3272 * Note: FirstShared() is already the new first */
3273 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
3276 this->next_shared = nullptr;
3277 this->previous_shared = nullptr;
3280 void VehiclesYearlyLoop()
3282 Vehicle *v;
3283 FOR_ALL_VEHICLES(v) {
3284 if (v->IsPrimaryVehicle()) {
3285 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
3286 Money profit = v->GetDisplayProfitThisYear();
3287 if (v->age >= 730 && profit < 0) {
3288 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
3289 SetDParam(0, v->index);
3290 SetDParam(1, profit);
3291 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
3293 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
3296 v->profit_last_year = v->profit_this_year;
3297 v->profit_lifetime += v->profit_this_year;
3298 v->profit_this_year = 0;
3299 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
3302 GroupStatistics::UpdateProfits();
3303 SetWindowClassesDirty(WC_TRAINS_LIST);
3304 SetWindowClassesDirty(WC_TRACE_RESTRICT_SLOTS);
3305 SetWindowClassesDirty(WC_SHIPS_LIST);
3306 SetWindowClassesDirty(WC_ROADVEH_LIST);
3307 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
3312 * Can this station be used by the given engine type?
3313 * @param engine_type the type of vehicles to test
3314 * @param st the station to test for
3315 * @return true if and only if the vehicle of the type can use this station.
3316 * @note For road vehicles the Vehicle is needed to determine whether it can
3317 * use the station. This function will return true for road vehicles
3318 * when at least one of the facilities is available.
3320 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
3322 const Engine *e = Engine::GetIfValid(engine_type);
3323 assert(e != nullptr);
3325 switch (e->type) {
3326 case VEH_TRAIN:
3327 return (st->facilities & FACIL_TRAIN) != 0;
3329 case VEH_ROAD:
3330 /* For road vehicles we need the vehicle to know whether it can actually
3331 * use the station, but if it doesn't have facilities for RVs it is
3332 * certainly not possible that the station can be used. */
3333 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
3335 case VEH_SHIP:
3336 return (st->facilities & FACIL_DOCK) != 0;
3338 case VEH_AIRCRAFT:
3339 return (st->facilities & FACIL_AIRPORT) != 0 &&
3340 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
3342 default:
3343 return false;
3348 * Can this station be used by the given vehicle?
3349 * @param v the vehicle to test
3350 * @param st the station to test for
3351 * @return true if and only if the vehicle can use this station.
3353 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
3355 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != nullptr;
3357 return CanVehicleUseStation(v->engine_type, st);
3361 * Access the ground vehicle cache of the vehicle.
3362 * @pre The vehicle is a #GroundVehicle.
3363 * @return #GroundVehicleCache of the vehicle.
3365 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
3367 assert(this->IsGroundVehicle());
3368 if (this->type == VEH_TRAIN) {
3369 return &Train::From(this)->gcache;
3370 } else {
3371 return &RoadVehicle::From(this)->gcache;
3376 * Access the ground vehicle cache of the vehicle.
3377 * @pre The vehicle is a #GroundVehicle.
3378 * @return #GroundVehicleCache of the vehicle.
3380 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
3382 assert(this->IsGroundVehicle());
3383 if (this->type == VEH_TRAIN) {
3384 return &Train::From(this)->gcache;
3385 } else {
3386 return &RoadVehicle::From(this)->gcache;
3391 * Access the ground vehicle flags of the vehicle.
3392 * @pre The vehicle is a #GroundVehicle.
3393 * @return #GroundVehicleFlags of the vehicle.
3395 uint16 &Vehicle::GetGroundVehicleFlags()
3397 assert(this->IsGroundVehicle());
3398 if (this->type == VEH_TRAIN) {
3399 return Train::From(this)->gv_flags;
3400 } else {
3401 return RoadVehicle::From(this)->gv_flags;
3406 * Access the ground vehicle flags of the vehicle.
3407 * @pre The vehicle is a #GroundVehicle.
3408 * @return #GroundVehicleFlags of the vehicle.
3410 const uint16 &Vehicle::GetGroundVehicleFlags() const
3412 assert(this->IsGroundVehicle());
3413 if (this->type == VEH_TRAIN) {
3414 return Train::From(this)->gv_flags;
3415 } else {
3416 return RoadVehicle::From(this)->gv_flags;
3421 * Calculates the set of vehicles that will be affected by a given selection.
3422 * @param set [inout] Set of affected vehicles.
3423 * @param v First vehicle of the selection.
3424 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
3425 * @pre \a set must be empty.
3426 * @post \a set will contain the vehicles that will be refitted.
3428 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
3430 if (v->type == VEH_TRAIN) {
3431 Train *u = Train::From(v);
3432 /* Only include whole vehicles, so start with the first articulated part */
3433 u = u->GetFirstEnginePart();
3435 /* Include num_vehicles vehicles, not counting articulated parts */
3436 for (; u != nullptr && num_vehicles > 0; num_vehicles--) {
3437 do {
3438 /* Include current vehicle in the selection. */
3439 set.Include(u->index);
3441 /* If the vehicle is multiheaded, add the other part too. */
3442 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
3444 u = u->Next();
3445 } while (u != nullptr && u->IsArticulatedPart());