Rework the trip history window layout.
[openttd-joker.git] / src / vehicle.cpp
blob002028121d97d371a760dea2d591bba0aab34423
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file vehicle.cpp Base implementations of all vehicles. */
12 #include "stdafx.h"
13 #include "error.h"
14 #include "roadveh.h"
15 #include "ship.h"
16 #include "spritecache.h"
17 #include "timetable.h"
18 #include "viewport_func.h"
19 #include "news_func.h"
20 #include "command_func.h"
21 #include "company_func.h"
22 #include "train.h"
23 #include "aircraft.h"
24 #include "newgrf_debug.h"
25 #include "newgrf_sound.h"
26 #include "newgrf_station.h"
27 #include "group_gui.h"
28 #include "strings_func.h"
29 #include "zoom_func.h"
30 #include "date_func.h"
31 #include "vehicle_func.h"
32 #include "autoreplace_func.h"
33 #include "autoreplace_gui.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "depot_func.h"
37 #include "network/network.h"
38 #include "core/pool_func.hpp"
39 #include "economy_base.h"
40 #include "articulated_vehicles.h"
41 #include "roadstop_base.h"
42 #include "depot_base.h"
43 #include "core/random_func.hpp"
44 #include "core/backup_type.hpp"
45 #include "order_backup.h"
46 #include "sound_func.h"
47 #include "effectvehicle_func.h"
48 #include "effectvehicle_base.h"
49 #include "vehiclelist.h"
50 #include "bridge_map.h"
51 #include "tunnel_map.h"
52 #include "depot_map.h"
53 #include "gamelog.h"
54 #include "tracerestrict.h"
55 #include "linkgraph/linkgraph.h"
56 #include "linkgraph/refresh.h"
57 #include "blitter/factory.hpp"
59 #include "table/strings.h"
61 #include <algorithm>
63 #include "tbtr_template_vehicle_func.h"
64 #include "safeguards.h"
66 /* Number of bits in the hash to use from each vehicle coord */
67 static const uint GEN_HASHX_BITS = 6;
68 static const uint GEN_HASHY_BITS = 6;
70 /* Size of each hash bucket */
71 static const uint GEN_HASHX_BUCKET_BITS = 7;
72 static const uint GEN_HASHY_BUCKET_BITS = 6;
74 /* Compute hash for vehicle coord */
75 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
76 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
77 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
79 /* Maximum size until hash repeats */
80 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_LVL_SHIFT);
81 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_LVL_SHIFT);
83 /* Increments to reach next bucket in hash table */
84 static const int GEN_HASHX_INC = 1;
85 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
87 /* Mask to wrap-around buckets */
88 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
89 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
91 VehicleID _new_vehicle_id;
92 uint16 _returned_refit_capacity; ///< Stores the capacity after a refit operation.
93 uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refit operation (Aircraft only).
96 /** The pool with all our precious vehicles. */
97 VehiclePool _vehicle_pool("Vehicle");
98 INSTANTIATE_POOL_METHODS(Vehicle)
102 * Determine shared bounds of all sprites.
103 * @param [out] bounds Shared bounds.
105 Rect16 VehicleSpriteSeq::GetBounds() const
107 Rect16 bounds;
108 bounds.left = bounds.top = bounds.right = bounds.bottom = 0;
110 for (uint i = 0; i < this->count; ++i) {
111 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
113 if (i == 0) {
114 bounds.left = spr->x_offs;
115 bounds.top = spr->y_offs;
116 bounds.right = spr->width + spr->x_offs - 1;
117 bounds.bottom = spr->height + spr->y_offs - 1;
118 } else {
119 if (spr->x_offs < bounds.left) bounds.left = spr->x_offs;
120 if (spr->y_offs < bounds.top) bounds.top = spr->y_offs;
121 int right = spr->width + spr->x_offs - 1;
122 int bottom = spr->height + spr->y_offs - 1;
123 if (right > bounds.right) bounds.right = right;
124 if (bottom > bounds.bottom) bounds.bottom = bottom;
128 return bounds;
132 * Draw the sprite sequence.
133 * @param x X position
134 * @param y Y position
135 * @param default_pal Vehicle palette
136 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
138 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
140 for (uint i = 0; i < this->count; ++i) {
141 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
142 DrawSprite(this->seq[i].sprite, pal, x, y);
147 * Function to tell if a vehicle needs to be autorenewed
148 * @param *c The vehicle owner
149 * @param use_renew_setting Should the company renew setting be considered?
150 * @return true if the vehicle is old enough for replacement
152 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
154 /* We can always generate the Company pointer when we have the vehicle.
155 * However this takes time and since the Company pointer is often present
156 * when this function is called then it's faster to pass the pointer as an
157 * argument rather than finding it again. */
158 assert(c == Company::Get(this->owner));
160 if (use_renew_setting && !c->settings.engine_renew) return false;
161 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
163 /* Only engines need renewing */
164 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
166 return true;
170 * Service a vehicle and all subsequent vehicles in the consist
172 * @param *v The vehicle or vehicle chain being serviced
174 void VehicleServiceInDepot(Vehicle *v)
176 assert(v != NULL);
177 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
179 do {
180 v->date_of_last_service = _date;
181 v->breakdowns_since_last_service = 0;
182 v->reliability = v->GetEngine()->reliability;
183 /* Prevent vehicles from breaking down directly after exiting the depot. */
184 v->breakdown_chance = 0;
185 v->breakdown_ctr = 0;
186 v = v->Next();
187 } while (v != NULL && v->HasEngineType());
191 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
193 * @see NeedsAutomaticServicing()
194 * @return true if the vehicle should go to a depot if a opportunity presents itself.
196 bool Vehicle::NeedsServicing() const
198 /* Stopped or crashed vehicles will not move, as such making unmovable
199 * vehicles to go for service is lame. */
200 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
202 /* Are we ready for the next service cycle? */
203 const Company *c = Company::Get(this->owner);
205 if (this->ServiceIntervalIsPercent() ?
206 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
207 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
208 return false;
211 /* Do we even have any depots/hangars */
212 uint rail_pices = 0;
213 uint road_pieces = 0;
215 for (uint i = 0; i < lengthof(c->infrastructure.rail); i++) rail_pices += c->infrastructure.rail[i];
216 for (uint i = 0; i < lengthof(c->infrastructure.road); i++) road_pieces += c->infrastructure.road[i];
218 if ((this->type == VEH_TRAIN && rail_pices == 0) ||
219 (this->type == VEH_ROAD && road_pieces == 0) ||
220 (this->type == VEH_SHIP && c->infrastructure.water == 0) ||
221 (this->type == VEH_AIRCRAFT && c->infrastructure.airport == 0)) {
222 return false;
225 /* If we're servicing anyway, because we have not disabled servicing when
226 * there are no breakdowns or we are playing with breakdowns, bail out. */
227 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
228 _settings_game.difficulty.vehicle_breakdowns != 0) {
229 return true;
232 /* Is vehicle old and renewing is enabled */
233 if (this->NeedsAutorenewing(c, true)) {
234 return true;
237 /* Test whether there is some pending autoreplace.
238 * Note: We do this after the service-interval test.
239 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
240 bool pending_replace = false;
241 Money needed_money = c->settings.engine_renew_money;
242 if (needed_money > c->money) return false;
244 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
245 bool replace_when_old = false;
246 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
248 /* Check engine availability */
249 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
250 /* Is the vehicle old if we are not always replacing? */
251 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
253 /* Check refittability */
254 uint32 available_cargo_types, union_mask;
255 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
256 /* Is there anything to refit? */
257 if (union_mask != 0) {
258 CargoID cargo_type;
259 /* We cannot refit to mixed cargoes in an automated way */
260 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
262 /* Did the old vehicle carry anything? */
263 if (cargo_type != CT_INVALID) {
264 /* We can't refit the vehicle to carry the cargo we want */
265 if (!HasBit(available_cargo_types, cargo_type)) continue;
269 /* Check money.
270 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
271 pending_replace = true;
272 needed_money += 2 * Engine::Get(new_engine)->GetCost();
273 if (needed_money > c->money) return false;
276 return pending_replace;
280 * Checks if the current order should be interrupted for a service-in-depot order.
281 * @see NeedsServicing()
282 * @return true if the current order should be interrupted.
284 bool Vehicle::NeedsAutomaticServicing() const
286 if (this->HasDepotOrder()) return false;
287 if (this->current_order.IsType(OT_LOADING)) return false;
288 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
290 return NeedsServicing();
293 uint Vehicle::Crash(bool flooded)
295 assert((this->vehstatus & VS_CRASHED) == 0);
296 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
298 uint pass = 0;
299 /* Stop the vehicle. */
300 if (this->IsPrimaryVehicle())
301 this->vehstatus |= VS_STOPPED;
302 /* crash all wagons, and count passengers */
303 for (Vehicle *v = this; v != NULL; v = v->Next()) {
304 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
305 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
306 v->vehstatus |= VS_CRASHED;
307 v->MarkAllViewportsDirty();
310 /* Dirty some windows */
311 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
312 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
313 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
314 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
316 delete this->cargo_payment;
317 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
319 return RandomRange(pass + 1); // Randomise deceased passengers.
322 /** Marks the separation of this vehicle's order list invalid. */
323 void Vehicle::MarkSeparationInvalid()
325 if (this->orders.list != NULL) this->orders.list->MarkSeparationInvalid();
328 /** Sets new separation settings for this vehicle's shared orders. */
329 void Vehicle::SetSepSettings(TTSepMode Mode, uint Parameter)
331 if (this->orders.list != NULL) this->orders.list->SetSepSettings(Mode, Parameter);
335 * 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)
336 * @return whether to show vehicle
338 bool Vehicle::IsDrawn() const
340 return !(HasBit(this->subtype, GVSF_VIRTUAL)) && (!(this->vehstatus & VS_HIDDEN) ||
341 (IsTransparencySet(TO_TUNNELS) &&
342 ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) ||
343 (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE))));
347 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
348 * @param engine The engine that caused the problem
349 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
350 * @param part2 Part 2 of the error message, taking the engine as parameter 2
351 * @param bug_type Flag to check and set in grfconfig
352 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
354 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
356 const Engine *e = Engine::Get(engine);
357 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
359 /* Missing GRF. Nothing useful can be done in this situation. */
360 if (grfconfig == NULL) return;
362 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
363 SetBit(grfconfig->grf_bugs, bug_type);
364 SetDParamStr(0, grfconfig->GetName());
365 SetDParam(1, engine);
366 ShowErrorMessage(part1, part2, WL_CRITICAL);
367 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
370 /* debug output */
371 char buffer[512];
373 SetDParamStr(0, grfconfig->GetName());
374 GetString(buffer, part1, lastof(buffer));
375 DEBUG(grf, 0, "%s", buffer + 3);
377 SetDParam(1, engine);
378 GetString(buffer, part2, lastof(buffer));
379 DEBUG(grf, 0, "%s", buffer + 3);
383 * Logs a bug in GRF and shows a warning message if this
384 * is for the first time this happened.
385 * @param u first vehicle of chain
387 void VehicleLengthChanged(const Vehicle *u)
389 /* show a warning once for each engine in whole game and once for each GRF after each game load */
390 const Engine *engine = u->GetEngine();
391 uint32 grfid = engine->grf_prop.grffile->grfid;
392 GRFConfig *grfconfig = GetGRFConfig(grfid);
393 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
394 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
399 * Vehicle constructor.
400 * @param type Type of the new vehicle.
402 Vehicle::Vehicle(VehicleType type)
404 this->type = type;
405 this->coord.left = INVALID_COORD;
406 this->group_id = DEFAULT_GROUP;
407 this->fill_percent_te_id = INVALID_TE_ID;
408 this->first = this;
409 this->colourmap = PAL_NONE;
410 this->cargo_age_counter = 1;
411 this->last_station_visited = INVALID_STATION;
412 this->last_loading_station = INVALID_STATION;
416 * Get a value for a vehicle's random_bits.
417 * @return A random value from 0 to 255.
419 byte VehicleRandomBits()
421 return GB(Random(), 0, 8);
424 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
425 * lookup times at the expense of memory usage. */
426 const int HASH_BITS = 7;
427 const int HASH_SIZE = 1 << HASH_BITS;
428 const int HASH_MASK = HASH_SIZE - 1;
429 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
430 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
432 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
433 * Profiling results show that 0 is fastest. */
434 const int HASH_RES = 0;
436 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
438 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
440 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
441 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
442 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
443 for (; v != NULL; v = v->hash_tile_next) {
444 Vehicle *a = proc(v, data);
445 if (find_first && a != NULL) return a;
447 if (x == xu) break;
449 if (y == yu) break;
452 return NULL;
457 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
458 * @note Do not call this function directly!
459 * @param x The X location on the map
460 * @param y The Y location on the map
461 * @param data Arbitrary data passed to proc
462 * @param proc The proc that determines whether a vehicle will be "found".
463 * @param find_first Whether to return on the first found or iterate over
464 * all vehicles
465 * @return the best matching or first vehicle (depending on find_first).
467 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
469 const int COLL_DIST = 6;
471 /* Hash area to scan is from xl,yl to xu,yu */
472 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
473 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
474 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
475 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
477 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
481 * Find a vehicle from a specific location. It will call proc for ALL vehicles
482 * on the tile and YOU must make SURE that the "best one" is stored in the
483 * data value and is ALWAYS the same regardless of the order of the vehicles
484 * where proc was called on!
485 * When you fail to do this properly you create an almost untraceable DESYNC!
486 * @note The return value of proc will be ignored.
487 * @note Use this when you have the intention that all vehicles
488 * should be iterated over.
489 * @param x The X location on the map
490 * @param y The Y location on the map
491 * @param data Arbitrary data passed to proc
492 * @param proc The proc that determines whether a vehicle will be "found".
494 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
496 VehicleFromPosXY(x, y, data, proc, false);
500 * Checks whether a vehicle in on a specific location. It will call proc for
501 * vehicles until it returns non-NULL.
502 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
503 * should be iterated over.
504 * @param x The X location on the map
505 * @param y The Y location on the map
506 * @param data Arbitrary data passed to proc
507 * @param proc The proc that determines whether a vehicle will be "found".
508 * @return True if proc returned non-NULL.
510 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
512 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
516 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
517 * @note Do not call this function directly!
518 * @param tile The location on the map
519 * @param data Arbitrary data passed to \a proc.
520 * @param proc The proc that determines whether a vehicle will be "found".
521 * @param find_first Whether to return on the first found or iterate over
522 * all vehicles
523 * @return the best matching or first vehicle (depending on find_first).
525 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
527 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
528 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
530 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
531 for (; v != NULL; v = v->hash_tile_next) {
532 if (v->tile != tile) continue;
534 Vehicle *a = proc(v, data);
535 if (find_first && a != NULL) return a;
538 return NULL;
542 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
543 * on the tile and YOU must make SURE that the "best one" is stored in the
544 * data value and is ALWAYS the same regardless of the order of the vehicles
545 * where proc was called on!
546 * When you fail to do this properly you create an almost untraceable DESYNC!
547 * @note The return value of \a proc will be ignored.
548 * @note Use this function when you have the intention that all vehicles
549 * should be iterated over.
550 * @param tile The location on the map
551 * @param data Arbitrary data passed to \a proc.
552 * @param proc The proc that determines whether a vehicle will be "found".
554 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
556 VehicleFromPos(tile, data, proc, false);
560 * Checks whether a vehicle is on a specific location. It will call \a proc for
561 * vehicles until it returns non-NULL.
562 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
563 * should be iterated over.
564 * @param tile The location on the map
565 * @param data Arbitrary data passed to \a proc.
566 * @param proc The \a proc that determines whether a vehicle will be "found".
567 * @return True if proc returned non-NULL.
569 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
571 return VehicleFromPos(tile, data, proc, true) != NULL;
575 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
576 * @param v Vehicle to examine.
577 * @param data Pointer to height data.
578 * @return \a v if conditions are met, else \c NULL.
580 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
582 int z = *(int*)data;
584 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
585 if (v->z_pos > z) return NULL;
587 return v;
591 * Ensure there is no vehicle at the ground at the given position.
592 * @param tile Position to examine.
593 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
595 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
597 int z = GetTileMaxPixelZ(tile);
599 /* Value v is not safe in MP games, however, it is used to generate a local
600 * error message only (which may be different for different machines).
601 * Such a message does not affect MP synchronisation.
603 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
604 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
605 return CommandCost();
608 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
609 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
611 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
612 if (v == (const Vehicle *)data) return NULL;
614 return v;
618 * Finds vehicle in tunnel / bridge
619 * @param tile first end
620 * @param endtile second end
621 * @param ignore Ignore this vehicle when searching
622 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
624 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
626 /* Value v is not safe in MP games, however, it is used to generate a local
627 * error message only (which may be different for different machines).
628 * Such a message does not affect MP synchronisation.
630 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
631 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
633 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
634 return CommandCost();
637 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
639 TrackBits rail_bits = *(TrackBits *)data;
641 if (v->type != VEH_TRAIN) return NULL;
643 Train *t = Train::From(v);
644 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
646 return v;
650 * Tests if a vehicle interacts with the specified track bits.
651 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
653 * @param tile The tile.
654 * @param track_bits The track bits.
655 * @return \c true if no train that interacts, is found. \c false if a train is found.
657 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
659 /* Value v is not safe in MP games, however, it is used to generate a local
660 * error message only (which may be different for different machines).
661 * Such a message does not affect MP synchronisation.
663 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
664 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
665 return CommandCost();
668 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
670 Vehicle **old_hash = v->hash_tile_current;
671 Vehicle **new_hash;
673 if (remove) {
674 new_hash = NULL;
675 } else {
676 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
677 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
678 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
681 if (old_hash == new_hash) return;
683 /* Remove from the old position in the hash table */
684 if (old_hash != NULL) {
685 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
686 *v->hash_tile_prev = v->hash_tile_next;
689 /* Insert vehicle at beginning of the new position in the hash table */
690 if (new_hash != NULL) {
691 v->hash_tile_next = *new_hash;
692 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
693 v->hash_tile_prev = new_hash;
694 *new_hash = v;
697 /* Remember current hash position */
698 v->hash_tile_current = new_hash;
701 static Vehicle *_vehicle_viewport_hash[1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)];
703 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
705 Vehicle **old_hash, **new_hash;
706 int old_x = v->coord.left;
707 int old_y = v->coord.top;
709 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
710 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
712 if (old_hash == new_hash) return;
714 /* remove from hash table? */
715 if (old_hash != NULL) {
716 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
717 *v->hash_viewport_prev = v->hash_viewport_next;
720 /* insert into hash table? */
721 if (new_hash != NULL) {
722 v->hash_viewport_next = *new_hash;
723 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
724 v->hash_viewport_prev = new_hash;
725 *new_hash = v;
729 void ResetVehicleHash()
731 Vehicle *v;
732 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
733 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
734 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
737 void ResetVehicleColourMap()
739 Vehicle *v;
740 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
744 * List of vehicles that should check for autoreplace this tick.
745 * Mapping of vehicle -> leave depot immediately after autoreplace.
747 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
748 static AutoreplaceMap _vehicles_to_autoreplace;
751 * List of vehicles that are issued for template replacement this tick.
752 * Mapping is {vehicle : leave depot after replacement}
754 typedef SmallMap<Train *, bool, 4> TemplateReplacementMap;
755 static TemplateReplacementMap _vehicles_to_templatereplace;
757 void InitializeVehicles()
759 _vehicles_to_autoreplace.Reset();
760 ResetVehicleHash();
763 uint CountVehiclesInChain(const Vehicle *v)
765 uint count = 0;
766 do count++; while ((v = v->Next()) != NULL);
767 return count;
771 * Check if a vehicle is counted in num_engines in each company struct
772 * @return true if the vehicle is counted in num_engines
774 bool Vehicle::IsEngineCountable() const
776 if (HasBit(this->subtype, GVSF_VIRTUAL)) return false;
777 switch (this->type) {
778 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
779 case VEH_TRAIN:
780 return !this->IsArticulatedPart() && // tenders and other articulated parts
781 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
782 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
783 case VEH_SHIP: return true;
784 default: return false; // Only count company buildable vehicles
789 * Check whether Vehicle::engine_type has any meaning.
790 * @return true if the vehicle has a useable engine type.
792 bool Vehicle::HasEngineType() const
794 switch (this->type) {
795 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
796 case VEH_TRAIN:
797 case VEH_ROAD:
798 case VEH_SHIP: return true;
799 default: return false;
804 * Retrieves the engine of the vehicle.
805 * @return Engine of the vehicle.
806 * @pre HasEngineType() == true
808 const Engine *Vehicle::GetEngine() const
810 return Engine::Get(this->engine_type);
814 * Retrieve the NewGRF the vehicle is tied to.
815 * This is the GRF providing the Action 3 for the engine type.
816 * @return NewGRF associated to the vehicle.
818 const GRFFile *Vehicle::GetGRF() const
820 return this->GetEngine()->GetGRF();
824 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
825 * This is the GRF providing the Action 3 for the engine type.
826 * @return GRF ID of the associated NewGRF.
828 uint32 Vehicle::GetGRFID() const
830 return this->GetEngine()->GetGRFID();
834 * Handle the pathfinding result, especially the lost status.
835 * If the vehicle is now lost and wasn't previously fire an
836 * event to the AIs and a news message to the user. If the
837 * vehicle is not lost anymore remove the news message.
838 * @param path_found Whether the vehicle has a path to its destination.
840 void Vehicle::HandlePathfindingResult(bool path_found)
842 if (path_found) {
843 /* Route found, is the vehicle marked with "lost" flag? */
844 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
846 /* Clear the flag as the PF's problem was solved. */
847 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
848 /* Delete the news item. */
849 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
850 return;
853 /* Were we already lost? */
854 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
856 /* It is first time the problem occurred, set the "lost" flag. */
857 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
858 /* Notify user about the event. */
859 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
860 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
861 SetDParam(0, this->index);
862 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
866 /** Destroy all stuff that (still) needs the virtual functions to work properly */
867 void Vehicle::PreDestructor()
869 if (CleaningPool()) return;
871 if (Station::IsValidID(this->last_station_visited)) {
872 Station *st = Station::Get(this->last_station_visited);
873 st->loading_vehicles.erase(std::remove(st->loading_vehicles.begin(), st->loading_vehicles.end(), this), st->loading_vehicles.end());
875 HideFillingPercent(&this->fill_percent_te_id);
876 this->CancelReservation(INVALID_STATION, st);
877 delete this->cargo_payment;
878 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
881 if (this->IsEngineCountable()) {
882 GroupStatistics::CountEngine(this, -1);
883 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
884 GroupStatistics::UpdateAutoreplace(this->owner);
886 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
887 DeleteGroupHighlightOfVehicle(this);
888 if (this->type == VEH_TRAIN) {
889 extern void DeleteTraceRestrictSlotHighlightOfVehicle(const Vehicle *v);
891 DeleteTraceRestrictSlotHighlightOfVehicle(this);
895 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
896 Aircraft *a = Aircraft::From(this);
897 Station *st = GetTargetAirportIfValid(a);
898 if (st != NULL) {
899 const AirportFTA *layout = st->airport.GetFTA()->layout;
900 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
905 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
906 RoadVehicle *v = RoadVehicle::From(this);
907 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
908 /* Leave the drive through roadstop, when you have not already left it. */
909 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
913 if (this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_HAVE_SLOT)) {
914 TraceRestrictRemoveVehicleFromAllSlots(this->index);
915 ClrBit(Train::From(this)->flags, VRF_HAVE_SLOT);
918 if (this->Previous() == NULL) {
919 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
922 if (this->IsPrimaryVehicle()) {
923 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
924 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
925 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
926 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
927 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
928 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS, this->index);
929 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS, this->index);
930 SetWindowDirty(WC_COMPANY, this->owner);
931 OrderBackup::ClearVehicle(this);
933 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
935 this->cargo.Truncate();
936 this->DeleteVehicleOrders();
937 DeleteDepotHighlightOfVehicle(this);
939 extern void StopGlobalFollowVehicle(const Vehicle *v);
940 StopGlobalFollowVehicle(this);
942 ReleaseDisastersTargetingVehicle(this->index);
945 Vehicle::~Vehicle()
947 if (CleaningPool()) {
948 this->cargo.OnCleanPool();
949 return;
952 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
953 * it may happen that vehicle chain is deleted when visible */
954 if (this->IsDrawn()) this->MarkAllViewportsDirty();
956 Vehicle *v = this->Next();
957 this->SetNext(NULL);
959 delete v;
961 UpdateVehicleTileHash(this, true);
962 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
963 DeleteVehicleNews(this->index, INVALID_STRING_ID);
964 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
968 * Adds a vehicle to the list of vehicles that visited a depot this tick
969 * @param *v vehicle to add
971 void VehicleEnteredDepotThisTick(Vehicle *v)
973 /* Template Replacement Setup stuff */
974 bool stayInDepot = v->current_order.GetDepotActionType() != ODATF_SERVICE_ONLY;
975 TemplateReplacement *tr = GetTemplateReplacementByGroupID(v->group_id);
976 if ( tr ) {
977 if ( stayInDepot ) _vehicles_to_templatereplace[(Train*)v] = true;
978 else _vehicles_to_templatereplace[(Train*)v] = false;
980 /* Moved the assignment for auto replacement here to prevent auto replacement
981 * from happening if template replacement is also scheduled */
982 else
983 /* Vehicle should stop in the depot if it was in 'stopping' state */
984 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
986 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
987 * stopping in the depot, so we stop it to ensure that it will not reserve
988 * the path out of the depot before we might autoreplace it to a different
989 * engine. The new engine would not own the reserved path we store that we
990 * stopped the vehicle, so autoreplace can start it again */
993 if (v->HasOrdersList() && (HasBit(v->vehicle_flags, VF_SHOULD_GOTO_DEPOT) || HasBit(v->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT) ||
994 v->current_order.GetDepotOrderType() == ODTF_MANUAL)) {
995 for (int i = 0; i < v->GetNumOrders(); ++i) {
996 Order* order = v->GetOrderAt(i);
998 assert(order != nullptr);
1000 if (order->GetType() == OT_GOTO_DEPOT && order->GetDestination() == v->current_order.GetDestination()) {
1001 v->cur_implicit_order_index = i;
1002 v->cur_real_order_index = i;
1003 break;
1008 ClrBit(v->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
1009 ClrBit(v->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
1011 v->vehstatus |= VS_STOPPED;
1015 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
1016 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
1017 * so each day, all vehicles are processes in DAY_TICKS steps.
1019 static void RunVehicleDayProc()
1021 if (_game_mode != GM_NORMAL) return;
1023 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1024 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
1025 Vehicle *v = Vehicle::Get(i);
1026 if (v == NULL) continue;
1028 /* Call the 32-day callback if needed */
1029 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
1030 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
1031 if (callback != CALLBACK_FAILED) {
1032 if (HasBit(callback, 0)) {
1033 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
1036 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1037 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1038 if (callback != 0) v->First()->MarkDirty();
1040 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
1044 /* This is called once per day for each vehicle, but not in the first tick of the day */
1045 v->OnNewDay();
1049 static void ShowAutoReplaceAdviceMessage(const CommandCost &res, const Vehicle *v)
1051 StringID error_message = res.GetErrorMessage();
1052 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) return;
1054 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1056 StringID message;
1057 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1058 message = error_message;
1059 } else {
1060 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1063 SetDParam(0, v->index);
1064 SetDParam(1, error_message);
1065 AddVehicleAdviceNewsItem(message, v->index);
1068 void CallVehicleTicks()
1070 _vehicles_to_autoreplace.Clear();
1071 _vehicles_to_templatereplace.Clear();
1073 RunVehicleDayProc();
1075 Station *st;
1076 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
1078 Vehicle *v;
1079 FOR_ALL_VEHICLES(v) {
1080 /* Vehicle could be deleted in this tick */
1081 if (!v->Tick()) {
1082 assert(Vehicle::Get(vehicle_index) == NULL);
1083 continue;
1086 assert(Vehicle::Get(vehicle_index) == v);
1088 switch (v->type) {
1089 default: break;
1091 case VEH_TRAIN:
1092 case VEH_ROAD:
1093 case VEH_AIRCRAFT:
1094 case VEH_SHIP: {
1095 Vehicle *front = v->First();
1097 if (v->vcache.cached_cargo_age_period != 0) {
1098 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
1099 if (--v->cargo_age_counter == 0) {
1100 v->cargo.AgeCargo();
1101 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
1105 /* Do not play any sound when crashed */
1106 if (front->vehstatus & VS_CRASHED) continue;
1108 /* Do not play any sound when in depot or tunnel */
1109 if (v->vehstatus & VS_HIDDEN) continue;
1111 /* Do not play any sound when stopped */
1112 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1114 /* Check vehicle type specifics */
1115 switch (v->type) {
1116 case VEH_TRAIN:
1117 if (Train::From(v)->IsWagon()) continue;
1118 break;
1120 case VEH_ROAD:
1121 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1122 break;
1124 case VEH_AIRCRAFT:
1125 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1126 break;
1128 default:
1129 break;
1132 v->motion_counter += front->cur_speed;
1133 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1134 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1136 /* Play an alternating running sound every 16 ticks */
1137 if (GB(v->tick_counter, 0, 4) == 0) {
1138 /* Play running sound when speed > 0 and not braking */
1139 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1140 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1143 break;
1148 /* do Template Replacement */
1149 Backup<CompanyByte> tmpl_cur_company(_current_company, FILE_LINE);
1150 for (TemplateReplacementMap::iterator it = _vehicles_to_templatereplace.Begin(); it != _vehicles_to_templatereplace.End(); it++) {
1151 Train *t = it->first;
1153 _vehicles_to_autoreplace.Erase(t);
1155 /* Store the position of the effect as the vehicle pointer will become invalid later */
1156 int x = t->x_pos;
1157 int y = t->y_pos;
1158 int z = t->z_pos;
1160 tmpl_cur_company.Change(t->owner);
1162 bool stayInDepot = it->second;
1164 it->first->vehstatus |= VS_STOPPED;
1165 CommandCost res = DoCommand(t->tile, t->index, stayInDepot ? 1 : 0, DC_EXEC, CMD_TEMPLATE_REPLACE_VEHICLE);
1167 if (res.Succeeded()) {
1168 VehicleID t_new = _new_vehicle_id;
1169 t = Train::From(Vehicle::Get(t_new));
1170 const Company *c = Company::Get(_current_company);
1171 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1172 CommandCost res2 = DoCommand(0, t_new, 1, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1173 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1174 if (res2.Succeeded() || res.GetCost() == 0) res.AddCost(res2);
1177 if (!IsLocalCompany()) continue;
1179 if (res.Succeeded()) {
1180 if (res.GetCost() != 0) {
1181 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1183 continue;
1186 ShowAutoReplaceAdviceMessage(res, t);
1188 tmpl_cur_company.Restore();
1190 /* do Auto Replacement */
1191 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1192 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1193 v = it->first;
1194 /* Autoreplace needs the current company set as the vehicle owner */
1195 cur_company.Change(v->owner);
1197 if (v->type == VEH_TRAIN) {
1198 assert(!_vehicles_to_templatereplace.Contains(Train::From(v)));
1201 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1202 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1203 * they are already leaving the depot again before being replaced. */
1204 if (it->second) v->vehstatus &= ~VS_STOPPED;
1206 /* Store the position of the effect as the vehicle pointer will become invalid later */
1207 int x = v->x_pos;
1208 int y = v->y_pos;
1209 int z = v->z_pos;
1211 const Company *c = Company::Get(_current_company);
1212 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1213 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1214 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1216 if (!IsLocalCompany()) continue;
1218 if (res.Succeeded()) {
1219 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1220 continue;
1223 ShowAutoReplaceAdviceMessage(res, v);
1225 cur_company.Restore();
1229 * Add vehicle sprite for drawing to the screen.
1230 * @param v Vehicle to draw.
1232 static void DoDrawVehicle(const Vehicle *v)
1234 PaletteID pal = PAL_NONE;
1236 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1238 /* Check whether the vehicle shall be transparent due to the game state */
1239 bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN)) != 0;
1241 if (v->type == VEH_EFFECT) {
1242 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1243 * However, transparent smoke and bubbles look weird, so always hide them. */
1244 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1245 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1248 StartSpriteCombine();
1249 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1250 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1251 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1252 AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1253 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1255 EndSpriteCombine();
1258 struct ViewportHashBound {
1259 int xl, xu, yl, yu;
1262 static ViewportHashBound GetViewportHashBound(int l, int r, int t, int b) {
1263 int xl = (l - (70 * ZOOM_LVL_BASE)) >> (7 + ZOOM_LVL_SHIFT);
1264 int xu = (r ) >> (7 + ZOOM_LVL_SHIFT);
1265 /* compare after shifting instead of before, so that lower bits don't affect comparison result */
1266 if (xu - xl < (1 << 6)) {
1267 xl &= 0x3F;
1268 xu &= 0x3F;
1269 } else {
1270 /* scan whole hash row */
1271 xl = 0;
1272 xu = 0x3F;
1275 int yl = (t - (70 * ZOOM_LVL_BASE)) >> (6 + ZOOM_LVL_SHIFT);
1276 int yu = (b ) >> (6 + ZOOM_LVL_SHIFT);
1277 /* compare after shifting instead of before, so that lower bits don't affect comparison result */
1278 if (yu - yl < (1 << 6)) {
1279 yl = (yl & 0x3F) << 6;
1280 yu = (yu & 0x3F) << 6;
1281 } else {
1282 /* scan whole column */
1283 yl = 0;
1284 yu = 0x3F << 6;
1286 return { xl, xu, yl, yu };
1290 * Add the vehicle sprites that should be drawn at a part of the screen.
1291 * @param dpi Rectangle being drawn.
1293 void ViewportAddVehicles(DrawPixelInfo *dpi)
1295 /* The bounding rectangle */
1296 const int l = dpi->left;
1297 const int r = dpi->left + dpi->width;
1298 const int t = dpi->top;
1299 const int b = dpi->top + dpi->height;
1301 /* The hash area to scan */
1302 const ViewportHashBound vhb = GetViewportHashBound(l, r, t, b);
1304 for (int y = vhb.yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1305 for (int x = vhb.xl;; x = (x + 1) & 0x3F) {
1306 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1308 while (v != NULL) {
1309 if (v->IsDrawn() &&
1310 l <= v->coord.right &&
1311 t <= v->coord.bottom &&
1312 r >= v->coord.left &&
1313 b >= v->coord.top) {
1314 DoDrawVehicle(v);
1316 v = v->hash_viewport_next;
1319 if (x == vhb.xu) break;
1322 if (y == vhb.yu) break;
1326 void ViewportMapDrawVehicles(DrawPixelInfo *dpi)
1328 /* The bounding rectangle */
1329 const int l = dpi->left;
1330 const int r = dpi->left + dpi->width;
1331 const int t = dpi->top;
1332 const int b = dpi->top + dpi->height;
1334 /* The hash area to scan */
1335 const ViewportHashBound vhb = GetViewportHashBound(l, r, t, b);
1337 const int w = UnScaleByZoom(dpi->width, dpi->zoom);
1338 const int h = UnScaleByZoom(dpi->height, dpi->zoom);
1339 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
1340 for (int y = vhb.yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1341 for (int x = vhb.xl;; x = (x + 1) & 0x3F) {
1342 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1344 while (v != NULL) {
1345 if (!(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) && (v->type != VEH_EFFECT)) {
1346 Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
1347 const int pixel_x = UnScaleByZoomLower(pt.x - dpi->left, dpi->zoom);
1348 if (IsInsideMM(pixel_x, 0, w)) {
1349 const int pixel_y = UnScaleByZoomLower(pt.y - dpi->top, dpi->zoom);
1350 if (IsInsideMM(pixel_y, 0, h))
1351 blitter->SetPixel(dpi->dst_ptr, pixel_x, pixel_y, PC_WHITE);
1354 v = v->hash_viewport_next;
1357 if (x == vhb.xu) break;
1360 if (y == vhb.yu) break;
1365 * Find the vehicle close to the clicked coordinates.
1366 * @param vp Viewport clicked in.
1367 * @param x X coordinate in the viewport.
1368 * @param y Y coordinate in the viewport.
1369 * @return Closest vehicle, or \c NULL if none found.
1371 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1373 Vehicle *found = NULL, *v;
1374 uint dist, best_dist = UINT_MAX;
1376 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1378 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1379 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1381 FOR_ALL_VEHICLES(v) {
1382 if (((v->vehstatus & VS_UNCLICKABLE) == 0) && v->IsDrawn() &&
1383 x >= v->coord.left && x <= v->coord.right &&
1384 y >= v->coord.top && y <= v->coord.bottom) {
1386 dist = max(
1387 abs(((v->coord.left + v->coord.right) >> 1) - x),
1388 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1391 if (dist < best_dist) {
1392 found = v;
1393 best_dist = dist;
1398 return found;
1402 * Decrease the value of a vehicle.
1403 * @param v %Vehicle to devaluate.
1405 void DecreaseVehicleValue(Vehicle *v)
1407 v->value -= v->value >> 8;
1408 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1411 static const byte _breakdown_chance[64] = {
1412 3, 3, 3, 3, 3, 3, 3, 3,
1413 4, 4, 5, 5, 6, 6, 7, 7,
1414 8, 8, 9, 9, 10, 10, 11, 11,
1415 12, 13, 13, 13, 13, 14, 15, 16,
1416 17, 19, 21, 25, 28, 31, 34, 37,
1417 40, 44, 48, 52, 56, 60, 64, 68,
1418 72, 80, 90, 100, 110, 120, 130, 140,
1419 150, 170, 190, 210, 230, 250, 250, 250,
1422 void CheckVehicleBreakdown(Vehicle *v)
1424 int rel, rel_old;
1426 /* decrease reliability */
1427 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1428 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1430 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1431 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1432 v->cur_speed < 5 || _game_mode == GM_MENU) {
1433 return;
1436 uint32 r = Random();
1438 /* increase chance of failure */
1439 int chance = v->breakdown_chance + 1;
1440 if (Chance16I(1, 25, r)) chance += 25;
1441 v->breakdown_chance = min(255, chance);
1443 /* calculate reliability value to use in comparison */
1444 rel = v->reliability;
1445 if (v->type == VEH_SHIP) rel += 0x6666;
1447 /* reduced breakdowns? */
1448 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1450 /* check if to break down */
1451 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1452 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1453 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1454 v->breakdown_chance = 0;
1459 * Handle all of the aspects of a vehicle breakdown
1460 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1461 * @return true iff the vehicle is stopped because of a breakdown
1462 * @note This function always returns false for aircraft, since these never stop for breakdowns
1464 bool Vehicle::HandleBreakdown()
1466 /* Possible states for Vehicle::breakdown_ctr
1467 * 0 - vehicle is running normally
1468 * 1 - vehicle is currently broken down
1469 * 2 - vehicle is going to break down now
1470 * >2 - vehicle is counting down to the actual breakdown event */
1471 switch (this->breakdown_ctr) {
1472 case 0:
1473 return false;
1475 case 2:
1476 this->breakdown_ctr = 1;
1478 if (this->breakdowns_since_last_service != 255) {
1479 this->breakdowns_since_last_service++;
1482 if (this->type == VEH_AIRCRAFT) {
1483 /* Aircraft just need this flag, the rest is handled elsewhere */
1484 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1485 } else {
1486 this->cur_speed = 0;
1488 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1489 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1490 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1491 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1492 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1495 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1496 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1497 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1501 this->MarkDirty(); // Update graphics after speed is zeroed
1502 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1503 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1505 FALLTHROUGH;
1506 case 1:
1507 /* Aircraft breakdowns end only when arriving at the airport */
1508 if (this->type == VEH_AIRCRAFT) return false;
1510 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1511 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1512 if (--this->breakdown_delay == 0) {
1513 this->breakdown_ctr = 0;
1514 this->MarkDirty();
1515 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1518 return true;
1520 default:
1521 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1522 return false;
1527 * Update age of a vehicle.
1528 * @param v Vehicle to update.
1530 void AgeVehicle(Vehicle *v)
1532 /* Stop if a virtual vehicle */
1533 if (HasBit(v->subtype, GVSF_VIRTUAL)) return;
1535 if (v->age < MAX_DAY) {
1536 v->age++;
1537 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1540 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1542 int age = v->age - v->max_age;
1543 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1544 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1545 v->reliability_spd_dec <<= 1;
1548 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1550 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1551 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1553 /* Don't warn if a renew is active */
1554 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1556 StringID str;
1557 if (age == -DAYS_IN_LEAP_YEAR) {
1558 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1559 } else if (age == 0) {
1560 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1561 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1562 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1563 } else {
1564 return;
1567 SetDParam(0, v->index);
1568 AddVehicleAdviceNewsItem(str, v->index);
1572 * Calculates how full a vehicle is.
1573 * @param front The front vehicle of the consist to check.
1574 * @param colour The string to show depending on if we are unloading or loading
1575 * @return A percentage of how full the Vehicle is.
1576 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1577 * if the vehicle is completely empty or full.
1578 * This is useful for both display and conditional orders.
1580 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1582 int count = 0;
1583 int max = 0;
1584 int cars = 0;
1585 int unloading = 0;
1586 bool loading = false;
1588 bool is_loading = front->current_order.IsType(OT_LOADING);
1590 /* The station may be NULL when the (colour) string does not need to be set. */
1591 const Station *st = Station::GetIfValid(front->last_station_visited);
1592 assert(colour == NULL || (st != NULL && is_loading));
1594 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1595 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1597 /* Count up max and used */
1598 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1599 count += v->cargo.StoredCount();
1600 max += v->cargo_cap;
1601 if (v->cargo_cap != 0 && colour != NULL) {
1602 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1603 loading |= !order_no_load &&
1604 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1605 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1606 cars++;
1610 if (colour != NULL) {
1611 if (unloading == 0 && loading) {
1612 *colour = STR_PERCENT_UP;
1613 } else if (unloading == 0 && !loading) {
1614 *colour = STR_PERCENT_NONE;
1615 } else if (cars == unloading || !loading) {
1616 *colour = STR_PERCENT_DOWN;
1617 } else {
1618 *colour = STR_PERCENT_UP_DOWN;
1622 /* Train without capacity */
1623 if (max == 0) return 100;
1625 /* Return the percentage */
1626 if (count * 2 < max) {
1627 /* Less than 50%; round up, so that 0% means really empty. */
1628 return CeilDiv(count * 100, max);
1629 } else {
1630 /* More than 50%; round down, so that 100% means really full. */
1631 return (count * 100) / max;
1636 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1637 * @param v Vehicle that entered a depot.
1639 void VehicleEnterDepot(Vehicle *v)
1641 /* Always work with the front of the vehicle */
1642 assert(v == v->First());
1644 switch (v->type) {
1645 case VEH_TRAIN: {
1646 Train *t = Train::From(v);
1647 SetWindowClassesDirty(WC_TRAINS_LIST);
1648 /* Clear path reservation */
1649 SetDepotReservation(t->tile, false);
1650 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile, ZOOM_LVL_DRAW_MAP);
1652 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1653 t->wait_counter = 0;
1654 t->force_proceed = TFP_NONE;
1655 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1656 t->ConsistChanged(CCF_ARRANGE);
1657 t->reverse_distance = 0;
1658 break;
1661 case VEH_ROAD:
1662 SetWindowClassesDirty(WC_ROADVEH_LIST);
1663 break;
1665 case VEH_SHIP: {
1666 SetWindowClassesDirty(WC_SHIPS_LIST);
1667 Ship *ship = Ship::From(v);
1668 ship->state = TRACK_BIT_DEPOT;
1669 ship->UpdateCache();
1670 ship->UpdateViewport(true, true);
1671 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1672 break;
1675 case VEH_AIRCRAFT:
1676 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1677 HandleAircraftEnterHangar(Aircraft::From(v));
1678 break;
1679 default: NOT_REACHED();
1681 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1683 if (v->type != VEH_TRAIN) {
1684 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1685 * 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 */
1686 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1688 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1690 v->vehstatus |= VS_HIDDEN;
1691 v->cur_speed = 0;
1693 VehicleServiceInDepot(v);
1695 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1696 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1697 v->MarkDirty();
1699 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1700 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1702 if (v->type != VEH_TRAIN && v->current_order.GetDepotOrderType() == ODTF_MANUAL) {
1703 // Check first if the vehicle has any depot in its order list. If yes then we're heading for a specific depot.
1704 // Don't stop if this one isn't it.
1705 bool has_depot_in_orders = false;
1707 for (int i = 0; i < v->GetNumOrders(); ++i) {
1708 Order* order = v->GetOrderAt(i);
1710 bool isRegularOrder = (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
1711 bool isDepotOrder = order->GetType() == OT_GOTO_DEPOT;
1713 if (isRegularOrder && isDepotOrder) {
1714 has_depot_in_orders = true;
1715 break;
1719 if (has_depot_in_orders)
1721 if ((v->type == VEH_AIRCRAFT && Station::GetByTile(v->dest_tile)->index != Station::GetByTile(v->tile)->index) ||
1722 (v->type != VEH_AIRCRAFT && v->dest_tile != v->tile)) {
1723 /* We are heading for another depot, keep driving. */
1724 return;
1729 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1731 /* Test whether we are heading for this depot. If not, do nothing.
1732 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1733 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1734 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1735 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1736 /* We are heading for another depot, keep driving. */
1737 return;
1740 if (v->current_order.IsRefit()) {
1741 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1742 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1743 cur_company.Restore();
1745 if (cost.Failed()) {
1746 _vehicles_to_autoreplace[v] = false;
1747 if (v->owner == _local_company) {
1748 /* Notify the user that we stopped the vehicle */
1749 SetDParam(0, v->index);
1750 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1752 } else if (cost.GetCost() != 0) {
1753 v->profit_this_year -= cost.GetCost() << 8;
1754 if (v->owner == _local_company) {
1755 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1760 /* Handle the ODTFB_PART_OF_ORDERS case. If there is a timetabled wait time, hold the vehicle, otherwise skip to the next order.
1761 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,
1762 he doesn't wait in it, as it would in stations. Thus, the original behavior is maintained if there's no defined wait_time.*/
1763 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1764 v->DeleteUnreachedImplicitOrders();
1765 UpdateVehicleTimetable(v, true);
1766 if (v->current_order.IsWaitTimetabled() && !(v->current_order.GetDepotActionType() & ODATFB_HALT)) {
1767 v->current_order.MakeWaiting();
1768 v->HandleAutomaticTimetableSeparation();
1769 v->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1770 return;
1772 else {
1773 UpdateVehicleTimetable(v, false);
1774 v->IncrementImplicitOrderIndex();
1777 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1778 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1779 _vehicles_to_autoreplace[v] = false;
1780 /* Invalidate last_loading_station. As the link from the station
1781 * before the stop to the station after the stop can't be predicted
1782 * we shouldn't construct it when the vehicle visits the next stop. */
1783 v->last_loading_station = INVALID_STATION;
1784 ClrBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP);
1785 if (v->owner == _local_company) {
1786 SetDParam(0, v->index);
1787 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1789 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1790 v->MarkSeparationInvalid();
1792 v->current_order.MakeDummy();
1798 * Update the position of the vehicle. This will update the hash that tells
1799 * which vehicles are on a tile.
1801 void Vehicle::UpdatePosition()
1803 UpdateVehicleTileHash(this, false);
1807 * Update the vehicle on the viewport, updating the right hash and setting the
1808 * new coordinates.
1809 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1811 void Vehicle::UpdateViewport(bool dirty)
1813 Rect new_coord = ConvertRect<Rect16, Rect>(this->sprite_seq_bounds);
1815 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1816 new_coord.left += pt.x;
1817 new_coord.top += pt.y;
1818 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1819 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1821 UpdateVehicleViewportHash(this, new_coord.left, new_coord.top);
1823 Rect old_coord = this->coord;
1824 this->coord = new_coord;
1826 if (dirty) {
1827 if (old_coord.left == INVALID_COORD) {
1828 this->MarkAllViewportsDirty();
1829 } else {
1830 ::MarkAllViewportsDirty(
1831 min(old_coord.left, this->coord.left),
1832 min(old_coord.top, this->coord.top),
1833 max(old_coord.right, this->coord.right),
1834 max(old_coord.bottom, this->coord.bottom),
1835 this->type != VEH_EFFECT ? ZOOM_LVL_END : ZOOM_LVL_DRAW_MAP);
1841 * Update the position of the vehicle, and update the viewport.
1843 void Vehicle::UpdatePositionAndViewport()
1845 this->UpdatePosition();
1846 this->UpdateViewport(true);
1850 * Marks viewports dirty where the vehicle's image is.
1852 void Vehicle::MarkAllViewportsDirty() const
1854 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom, this->type != VEH_EFFECT ? ZOOM_LVL_END : ZOOM_LVL_DRAW_MAP);
1858 * Get position information of a vehicle when moving one pixel in the direction it is facing
1859 * @param v Vehicle to move
1860 * @return Position information after the move
1862 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1864 static const int8 _delta_coord[16] = {
1865 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1866 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1869 int x = v->x_pos + _delta_coord[v->direction];
1870 int y = v->y_pos + _delta_coord[v->direction + 8];
1872 GetNewVehiclePosResult gp;
1873 gp.x = x;
1874 gp.y = y;
1875 gp.old_tile = v->tile;
1876 gp.new_tile = TileVirtXY(x, y);
1877 return gp;
1880 static const Direction _new_direction_table[] = {
1881 DIR_N, DIR_NW, DIR_W,
1882 DIR_NE, DIR_SE, DIR_SW,
1883 DIR_E, DIR_SE, DIR_S
1886 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1888 int i = 0;
1890 if (y >= v->y_pos) {
1891 if (y != v->y_pos) i += 3;
1892 i += 3;
1895 if (x >= v->x_pos) {
1896 if (x != v->x_pos) i++;
1897 i++;
1900 Direction dir = v->direction;
1902 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1903 if (dirdiff == DIRDIFF_SAME) return dir;
1904 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1908 * Call the tile callback function for a vehicle entering a tile
1909 * @param v Vehicle entering the tile
1910 * @param tile Tile entered
1911 * @param x X position
1912 * @param y Y position
1913 * @return Some meta-data over the to be entered tile.
1914 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1916 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1918 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1922 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1923 * struct initialization, except after each call to this->NextID() the returned value
1924 * is assigned to a vehicle.
1925 * @param type type of vehicle
1926 * @param owner owner of vehicles
1928 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1930 /* Find maximum */
1931 const Vehicle *v;
1932 FOR_ALL_VEHICLES(v) {
1933 if (v->type == type && v->owner == owner) {
1934 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1938 if (this->maxid == 0) return;
1940 /* Reserving 'maxid + 2' because we need:
1941 * - space for the last item (with v->unitnumber == maxid)
1942 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1943 this->cache = CallocT<bool>(this->maxid + 2);
1945 /* Fill the cache */
1946 FOR_ALL_VEHICLES(v) {
1947 if (v->type == type && v->owner == owner) {
1948 this->cache[v->unitnumber] = true;
1953 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1954 UnitID FreeUnitIDGenerator::NextID()
1956 if (this->maxid <= this->curid) return ++this->curid;
1958 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1960 return this->curid;
1964 * Get an unused unit number for a vehicle (if allowed).
1965 * @param type Type of vehicle
1966 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1968 UnitID GetFreeUnitNumber(VehicleType type)
1970 /* Check whether it is allowed to build another vehicle. */
1971 uint max_veh;
1972 switch (type) {
1973 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1974 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1975 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1976 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1977 default: NOT_REACHED();
1980 const Company *c = Company::Get(_current_company);
1981 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1983 FreeUnitIDGenerator gen(type, _current_company);
1985 return gen.NextID();
1990 * Check whether we can build infrastructure for the given
1991 * vehicle type. This to disable building stations etc. when
1992 * you are not allowed/able to have the vehicle type yet.
1993 * @param type the vehicle type to check this for
1994 * @return true if there is any reason why you may build
1995 * the infrastructure for the given vehicle type
1997 bool CanBuildVehicleInfrastructure(VehicleType type)
1999 assert(IsCompanyBuildableVehicleType(type));
2001 if (!Company::IsValidID(_local_company)) return false;
2002 if (!_settings_client.gui.disable_unsuitable_building) return true;
2004 UnitID max;
2005 switch (type) {
2006 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
2007 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
2008 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
2009 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
2010 default: NOT_REACHED();
2013 /* We can build vehicle infrastructure when we may build the vehicle type */
2014 if (max > 0) {
2015 /* Can we actually build the vehicle type? */
2016 const Engine *e;
2017 FOR_ALL_ENGINES_OF_TYPE(e, type) {
2018 if (HasBit(e->company_avail, _local_company)) return true;
2020 return false;
2023 /* We should be able to build infrastructure when we have the actual vehicle type */
2024 const Vehicle *v;
2025 FOR_ALL_VEHICLES(v) {
2026 if (v->owner == _local_company && v->type == type) return true;
2029 return false;
2034 * Determines the #LiveryScheme for a vehicle.
2035 * @param engine_type Engine of the vehicle.
2036 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
2037 * @param v the vehicle, \c NULL if in purchase list etc.
2038 * @return livery scheme to use.
2040 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
2042 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
2043 const Engine *e = Engine::Get(engine_type);
2044 switch (e->type) {
2045 default: NOT_REACHED();
2046 case VEH_TRAIN:
2047 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
2048 /* Wagonoverrides use the colour scheme of the front engine.
2049 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
2050 engine_type = parent_engine_type;
2051 e = Engine::Get(engine_type);
2052 /* Note: Luckily cargo_type is not needed for engines */
2055 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
2056 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
2057 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
2058 if (!CargoSpec::Get(cargo_type)->is_freight) {
2059 if (parent_engine_type == INVALID_ENGINE) {
2060 return LS_PASSENGER_WAGON_STEAM;
2061 } else {
2062 switch (RailVehInfo(parent_engine_type)->engclass) {
2063 default: NOT_REACHED();
2064 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
2065 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
2066 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
2067 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
2068 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
2071 } else {
2072 return LS_FREIGHT_WAGON;
2074 } else {
2075 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
2077 switch (e->u.rail.engclass) {
2078 default: NOT_REACHED();
2079 case EC_STEAM: return LS_STEAM;
2080 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
2081 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
2082 case EC_MONORAIL: return LS_MONORAIL;
2083 case EC_MAGLEV: return LS_MAGLEV;
2087 case VEH_ROAD:
2088 /* Always use the livery of the front */
2089 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
2090 engine_type = parent_engine_type;
2091 e = Engine::Get(engine_type);
2092 cargo_type = v->First()->cargo_type;
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
2097 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
2098 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
2099 /* Tram */
2100 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
2101 } else {
2102 /* Bus or truck */
2103 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
2106 case VEH_SHIP:
2107 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
2108 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
2109 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
2111 case VEH_AIRCRAFT:
2112 switch (e->u.air.subtype) {
2113 case AIR_HELI: return LS_HELICOPTER;
2114 case AIR_CTOL: return LS_SMALL_PLANE;
2115 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
2116 default: NOT_REACHED();
2122 * Determines the livery for a vehicle.
2123 * @param engine_type EngineID of the vehicle
2124 * @param company Owner of the vehicle
2125 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
2126 * @param v the vehicle. NULL if in purchase list etc.
2127 * @param livery_setting The livery settings to use for acquiring the livery information.
2128 * @return livery to use
2130 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
2132 const Company *c = Company::Get(company);
2133 LiveryScheme scheme = LS_DEFAULT;
2135 /* The default livery is always available for use, but its in_use flag determines
2136 * whether any _other_ liveries are in use. */
2137 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
2138 /* Determine the livery scheme to use */
2139 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
2141 /* Switch back to the default scheme if the resolved scheme is not in use */
2142 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
2145 return &c->livery[scheme];
2149 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
2151 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
2153 /* Return cached value if any */
2154 if (map != PAL_NONE) return map;
2156 const Engine *e = Engine::Get(engine_type);
2158 /* Check if we should use the colour map callback */
2159 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
2160 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
2161 /* Failure means "use the default two-colour" */
2162 if (callback != CALLBACK_FAILED) {
2163 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2164 map = GB(callback, 0, 14);
2165 /* If bit 14 is set, then the company colours are applied to the
2166 * map else it's returned as-is. */
2167 if (!HasBit(callback, 14)) {
2168 /* Update cache */
2169 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
2170 return map;
2175 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
2177 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
2179 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2180 if (!Company::IsValidID(company)) return map;
2182 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2184 map += livery->colour1;
2185 if (twocc) map += livery->colour2 * 16;
2187 /* Update cache */
2188 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
2189 return map;
2193 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2194 * @param engine_type ID of engine
2195 * @param company ID of company
2196 * @return A ready-to-use palette modifier
2198 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2200 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
2204 * Get the colour map for a vehicle.
2205 * @param v Vehicle to get colour map for
2206 * @return A ready-to-use palette modifier
2208 PaletteID GetVehiclePalette(const Vehicle *v)
2210 if (v->IsGroundVehicle()) {
2211 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
2214 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
2218 * Delete all implicit orders which were not reached.
2220 void Vehicle::DeleteUnreachedImplicitOrders()
2222 if (this->IsGroundVehicle()) {
2223 uint16 &gv_flags = this->GetGroundVehicleFlags();
2224 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2225 /* Do not delete orders, only skip them */
2226 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2227 this->cur_implicit_order_index = this->cur_real_order_index;
2228 InvalidateVehicleOrder(this, 0);
2229 return;
2233 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2234 while (order != NULL) {
2235 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2237 if (order->IsType(OT_IMPLICIT)) {
2238 DeleteOrder(this, this->cur_implicit_order_index);
2239 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2240 order = this->GetOrder(this->cur_implicit_order_index);
2241 } else {
2242 /* Skip non-implicit orders, e.g. service-orders */
2243 order = order->next;
2244 this->cur_implicit_order_index++;
2247 /* Wrap around */
2248 if (order == NULL) {
2249 order = this->GetOrder(0);
2250 this->cur_implicit_order_index = 0;
2256 * Increase capacity for all link stats associated with vehicles in the given consist.
2257 * @param st Station to get the link stats from.
2258 * @param front First vehicle in the consist.
2259 * @param next_station_id Station the consist will be travelling to next.
2261 static void VehicleIncreaseStats(const Vehicle *front)
2263 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
2264 StationID last_loading_station = HasBit(front->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? v->last_loading_station : front->last_loading_station;
2265 if (v->refit_cap > 0 &&
2266 last_loading_station != INVALID_STATION &&
2267 last_loading_station != front->last_station_visited &&
2268 ((front->current_order.GetCargoLoadType(v->cargo_type) & OLFB_NO_LOAD) == 0 ||
2269 (front->current_order.GetCargoUnloadType(v->cargo_type) & OUFB_NO_UNLOAD) == 0)) {
2270 /* The cargo count can indeed be higher than the refit_cap if
2271 * wagons have been auto-replaced and subsequently auto-
2272 * refitted to a higher capacity. The cargo gets redistributed
2273 * among the wagons in that case.
2274 * As usage is not such an important figure anyway we just
2275 * ignore the additional cargo then.*/
2276 IncreaseStats(Station::Get(last_loading_station), v->cargo_type, front->last_station_visited, v->refit_cap,
2277 min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
2283 * Prepare everything to begin the loading when arriving at a station.
2284 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2286 void Vehicle::BeginLoading()
2288 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2290 if (this->current_order.IsType(OT_GOTO_STATION) &&
2291 this->current_order.GetDestination() == this->last_station_visited) {
2292 this->DeleteUnreachedImplicitOrders();
2294 /* Now both order indices point to the destination station, and we can start loading */
2295 this->current_order.MakeLoading(true);
2296 UpdateVehicleTimetable(this, true);
2298 /* Furthermore add the Non Stop flag to mark that this station
2299 * is the actual destination of the vehicle, which is (for example)
2300 * necessary to be known for HandleTrainLoading to determine
2301 * whether the train is lost or not; not marking a train lost
2302 * that arrives at random stations is bad. */
2303 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2305 } else {
2306 /* We weren't scheduled to stop here. Insert an implicit order
2307 * to show that we are stopping here.
2308 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2309 * the 'wrong' terminal when skipping orders etc. */
2310 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2311 if (this->IsGroundVehicle() &&
2312 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2313 in_list->GetDestination() != this->last_station_visited)) {
2314 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2315 /* Do not create consecutive duplicates of implicit orders */
2316 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2317 if (prev_order == NULL ||
2318 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2319 prev_order->GetDestination() != this->last_station_visited) {
2321 /* Prefer deleting implicit orders instead of inserting new ones,
2322 * so test whether the right order follows later. In case of only
2323 * implicit orders treat the last order in the list like an
2324 * explicit one, except if the overall number of orders surpasses
2325 * IMPLICIT_ORDER_ONLY_CAP. */
2326 int target_index = this->cur_implicit_order_index;
2327 bool found = false;
2328 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2329 const Order *order = this->GetOrder(target_index);
2330 if (order == NULL) break; // No orders.
2331 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2332 found = true;
2333 break;
2335 target_index++;
2336 if (target_index >= this->GetNumOrders()) {
2337 if (this->GetNumManualOrders() == 0 &&
2338 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2339 break;
2341 target_index = 0;
2343 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2346 if (found) {
2347 if (suppress_implicit_orders) {
2348 /* Skip to the found order */
2349 this->cur_implicit_order_index = target_index;
2350 InvalidateVehicleOrder(this, 0);
2351 } else {
2352 /* Delete all implicit orders up to the station we just reached */
2353 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2354 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2355 if (order->IsType(OT_IMPLICIT)) {
2356 DeleteOrder(this, this->cur_implicit_order_index);
2357 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2358 order = this->GetOrder(this->cur_implicit_order_index);
2359 } else {
2360 /* Skip non-implicit orders, e.g. service-orders */
2361 order = order->next;
2362 this->cur_implicit_order_index++;
2365 /* Wrap around */
2366 if (order == NULL) {
2367 order = this->GetOrder(0);
2368 this->cur_implicit_order_index = 0;
2370 assert(order != NULL);
2373 } else if (!suppress_implicit_orders &&
2374 (this->HasOrdersList() ? this->GetNumOrders() < MAX_VEH_ORDER_ID : OrderList::CanAllocateItem()) &&
2375 Order::CanAllocateItem()) {
2376 /* Insert new implicit order */
2377 Order *implicit_order = new Order();
2378 implicit_order->MakeImplicit(this->last_station_visited);
2379 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2380 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2382 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2383 * Reenable it for this vehicle */
2384 uint16 &gv_flags = this->GetGroundVehicleFlags();
2385 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2390 this->current_order.MakeLoading(false);
2393 this->HandleAutomaticTimetableSeparation();
2395 VehicleIncreaseStats(this);
2397 PrepareUnload(this);
2399 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2400 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2401 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2402 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2404 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2405 this->cur_speed = 0;
2406 this->MarkDirty();
2410 * Handles the automatic timetable separation from initialization to setting of the lateness counter at the correct first order.
2412 void Vehicle::HandleAutomaticTimetableSeparation()
2414 /* If all requirements for separation are met, we can initialize it. */
2415 if (!_settings_game.order.automatic_timetable_separation) return;
2416 if (!this->HasSharedOrdersList()) return;
2417 assert(this->orders.list != nullptr);
2418 if (!this->orders.list->IsCompleteTimetable()) return;
2420 int first_wait_index = -1;
2422 for (int i = 0; i < this->GetNumOrders(); ++i) {
2423 Order* order = this->orders.list->GetOrderAt(i);
2425 if (order->IsWaitTimetabled() && !order->IsType(OT_IMPLICIT)) {
2426 first_wait_index = i;
2427 break;
2431 if (this->cur_implicit_order_index != first_wait_index) return;
2433 if (!this->orders.list->IsSeparationValid()) {
2434 this->orders.list->InitializeSeparation();
2435 this->lateness_counter = this->orders.list->SeparateVehicle();
2436 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
2438 else
2440 this->lateness_counter = this->orders.list->SeparateVehicle();
2442 if (this->lateness_counter > 0)
2444 this->orders.list->InitializeSeparation();
2445 this->lateness_counter = this->orders.list->SeparateVehicle();
2446 SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
2450 SetBit(this->vehicle_flags, VF_SEPARATION_IN_PROGRESS);
2454 * Return all reserved cargo packets to the station and reset all packets
2455 * staged for transfer.
2456 * @param st the station where the reserved packets should go.
2458 void Vehicle::CancelReservation(StationID next, Station *st)
2460 for (Vehicle *v = this; v != NULL; v = v->next) {
2461 VehicleCargoList &cargo = v->cargo;
2462 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2463 DEBUG(misc, 1, "cancelling cargo reservation");
2464 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2465 cargo.SetTransferLoadPlace(st->xy);
2467 cargo.KeepAll();
2471 uint32 Vehicle::GetLastLoadingStationValidCargoMask() const
2473 if (!HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP)) {
2474 return (this->last_loading_station != INVALID_STATION) ? ~0 : 0;
2475 } else {
2476 uint32 cargo_mask = 0;
2477 for (const Vehicle *u = this; u != NULL; u = u->Next()) {
2478 if (u->cargo_type < NUM_CARGO && u->last_loading_station != INVALID_STATION) {
2479 SetBit(cargo_mask, u->cargo_type);
2482 return cargo_mask;
2487 * Perform all actions when leaving a station.
2488 * @pre this->current_order.IsType(OT_LOADING)
2490 void Vehicle::LeaveStation()
2492 assert(this->current_order.IsType(OT_LOADING));
2494 delete this->cargo_payment;
2495 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2497 /* Only update the timetable if the vehicle was supposed to stop here. */
2498 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2500 uint32 cargoes_can_load_unload = this->current_order.FilterLoadUnloadTypeCargoMask([&](const Order *o, CargoID cargo) {
2501 return ((o->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0) || ((o->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0);
2503 uint32 has_cargo_mask = this->GetLastLoadingStationValidCargoMask();
2504 uint32 cargoes_can_leave_with_cargo = FilterCargoMask([&](CargoID cargo) {
2505 return this->current_order.CanLeaveWithCargo(HasBit(has_cargo_mask, cargo), cargo);
2506 }, cargoes_can_load_unload);
2508 if (cargoes_can_load_unload != 0) {
2509 if (cargoes_can_leave_with_cargo != 0) {
2510 /* Refresh next hop stats to make sure we've done that at least once
2511 * during the stop and that refit_cap == cargo_cap for each vehicle in
2512 * the consist. */
2513 this->ResetRefitCaps();
2514 LinkRefresher::Run(this, true, false, cargoes_can_leave_with_cargo);
2517 if (cargoes_can_leave_with_cargo == (uint32) ~0) {
2518 /* can leave with all cargoes */
2520 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2521 this->last_loading_station = this->last_station_visited;
2522 ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2523 } else if (cargoes_can_leave_with_cargo == 0) {
2524 /* can leave with no cargoes */
2526 /* if the vehicle couldn't load and had to unload or transfer everything
2527 * set the last loading station to invalid as it will leave empty. */
2528 this->last_loading_station = INVALID_STATION;
2529 ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2530 } else {
2531 /* mix of cargoes loadable or could not leave with all cargoes */
2533 /* NB: this is saved here as we overwrite it on the first iteration of the loop below */
2534 StationID head_last_loading_station = this->last_loading_station;
2535 for (Vehicle *u = this; u != NULL; u = u->Next()) {
2536 StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station;
2537 if (u->cargo_type < NUM_CARGO && HasBit(cargoes_can_load_unload, u->cargo_type)) {
2538 if (HasBit(cargoes_can_leave_with_cargo, u->cargo_type)) {
2539 u->last_loading_station = this->last_station_visited;
2540 } else {
2541 u->last_loading_station = INVALID_STATION;
2543 } else {
2544 u->last_loading_station = last_loading_station;
2547 SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
2552 int last_loading_order_index = -1;
2554 // Reverse iterate through the orders list and find the first (i.e. last) order that is of loading type.
2555 for (int i = this->GetNumOrders() - 1; i >= 0; --i) {
2556 Order* order = this->orders.list->GetOrderAt(i);
2558 bool can_load_or_unload = false;
2560 if ((order->IsType(OT_GOTO_STATION) || order->IsType(OT_IMPLICIT)) &&
2561 (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
2562 if (order->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || order->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
2563 can_load_or_unload = true;
2564 } else if ((order->GetLoadType() & OLFB_NO_LOAD) == 0 || (order->GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2565 can_load_or_unload = true;
2569 if (can_load_or_unload && !(order->GetLoadType() & OLFB_NO_LOAD)) {
2570 last_loading_order_index = i;
2571 break;
2575 if (last_loading_order_index >= 0 && last_loading_order_index < this->GetNumOrders()) {
2577 Order* current_real_order = this->orders.list->GetOrderAt(this->cur_real_order_index);
2579 bool can_load_or_unload = false;
2581 if ((current_real_order->IsType(OT_GOTO_STATION) || current_real_order->IsType(OT_IMPLICIT)) &&
2582 (current_real_order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
2583 if (current_real_order->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || current_real_order->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
2584 can_load_or_unload = true;
2586 else if ((current_real_order->GetLoadType() & OLFB_NO_LOAD) == 0 || (current_real_order->GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2587 can_load_or_unload = true;
2591 bool current_order_was_load_order = (can_load_or_unload && !(current_real_order->GetLoadType() & OLFB_NO_LOAD));
2593 if (current_order_was_load_order)
2595 int8 occupancy = (int8)CalcPercentVehicleFilled(this, NULL);
2596 station_occupancies.push_back(occupancy);
2599 bool wasLastLoadingOrder = this->cur_real_order_index == last_loading_order_index;
2601 if (wasLastLoadingOrder)
2603 if (station_occupancies.size() != 0)
2605 int sum = 0;
2607 std::vector<int8>::const_iterator it;
2608 for (it = station_occupancies.begin(); it != station_occupancies.end(); ++it)
2609 sum += *it;
2611 station_occupancies.clear();
2615 else {
2616 station_occupancies.clear();
2619 this->current_order.MakeLeaveStation();
2620 Station *st = Station::Get(this->last_station_visited);
2621 this->CancelReservation(INVALID_STATION, st);
2622 st->loading_vehicles.erase(std::remove(st->loading_vehicles.begin(), st->loading_vehicles.end(), this), st->loading_vehicles.end());
2624 HideFillingPercent(&this->fill_percent_te_id);
2626 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2627 /* Trigger station animation (trains only) */
2628 if (IsTileType(this->tile, MP_STATION)) {
2629 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2630 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2633 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2636 this->MarkDirty();
2640 * Reset all refit_cap in the consist to cargo_cap.
2642 void Vehicle::ResetRefitCaps()
2644 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2648 * Handle the loading of the vehicle; when not it skips through dummy
2649 * orders and does nothing in all other cases.
2650 * @param mode is the non-first call for this vehicle in this tick?
2652 void Vehicle::HandleLoading(bool mode)
2654 OrderType current_order_type = this->current_order.GetType();
2656 switch (current_order_type) {
2657 case OT_LOADING: {
2658 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2660 /* Save time just loading took since that is what goes into the timetable */
2661 if (!HasBit(this->vehicle_flags, VF_LOADING_FINISHED)) {
2662 this->current_loading_time = this->current_order_time;
2665 bool has_manual_depot_order = (HasBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT) || HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT));
2667 if (!has_manual_depot_order) {
2668 /* Not the first call for this tick, or still loading */
2669 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2672 this->PlayLeaveStationSound();
2674 this->LeaveStation();
2676 /* Only advance to next order if we just loaded at the current one */
2677 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2678 if (order == NULL ||
2679 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2680 order->GetDestination() != this->last_station_visited) {
2681 return;
2683 break;
2686 case OT_DUMMY: break;
2688 default: return;
2691 this->IncrementImplicitOrderIndex();
2695 * Handle the waiting time everywhere else as in stations (basically in depot but, eventually, also elsewhere ?)
2696 * Function is called when order's wait_time is defined.
2697 * @param stop_waiting should we stop waiting (or definitely avoid) even if there is still time left to wait ?
2699 void Vehicle::HandleWaiting(bool stop_waiting)
2701 switch (this->current_order.GetType()) {
2702 case OT_WAITING: {
2703 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2704 /* Vehicles holds on until waiting Timetabled time expires. */
2705 if (!stop_waiting && this->current_order_time < wait_time) {
2706 return;
2709 /* When wait_time is expired, we move on. */
2710 UpdateVehicleTimetable(this, false);
2711 this->IncrementImplicitOrderIndex();
2712 this->current_order.MakeDummy();
2714 break;
2717 default: return;
2722 * Send this vehicle to the depot using the given command(s).
2723 * @param flags the command flags (like execute and such).
2724 * @param command the command to execute.
2725 * @return the cost of the depot action.
2727 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2729 CommandCost ret = CheckOwnership(this->owner);
2730 if (ret.Failed()) return ret;
2732 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2733 if (this->IsStoppedInDepot()) return CMD_ERROR;
2735 if (HasBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT) || HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT)) {
2736 bool service_only = (command & DEPOT_SERVICE) != 0;
2737 if (service_only != HasBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT)) {
2738 /* We called with a different DEPOT_SERVICE setting.
2739 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2740 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2741 if (flags & DC_EXEC) {
2742 ClrBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
2743 ClrBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
2744 SetBit(this->vehicle_flags, service_only ? VF_SHOULD_SERVICE_AT_DEPOT : VF_SHOULD_GOTO_DEPOT);
2745 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2747 return CommandCost();
2750 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancellation of depot orders
2752 if(flags & DC_EXEC) {
2753 // Cancel the forced depot order.
2754 ClrBit(this->vehicle_flags, VF_SHOULD_GOTO_DEPOT);
2755 ClrBit(this->vehicle_flags, VF_SHOULD_SERVICE_AT_DEPOT);
2756 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2758 return CommandCost();
2760 } else if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() == ODTF_MANUAL) {
2761 bool current_service_only = !((this->current_order.GetDepotActionType() & ODATFB_HALT) != 0);
2762 bool service_only = (command & DEPOT_SERVICE) != 0;
2763 if (current_service_only != service_only) {
2764 /* We called with a different DEPOT_SERVICE setting.
2765 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2766 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2767 if (flags & DC_EXEC) {
2768 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2769 this->current_order.SetDepotActionType(service_only ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2770 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2772 return CommandCost();
2775 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancellation of depot orders
2776 if (flags & DC_EXEC) {
2777 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2778 * then skip to the next order; effectively canceling this forced service */
2779 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2781 if (this->IsGroundVehicle()) {
2782 uint16 &gv_flags = this->GetGroundVehicleFlags();
2783 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2786 this->current_order.MakeDummy();
2787 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2789 return CommandCost();
2792 TileIndex location;
2793 DestinationID destination;
2794 bool reverse;
2795 bool foundDepotInOrders = false;
2796 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};
2798 // Check first if the vehicle has any depot in its order list. Prefer that over the closest one.
2800 for (int i = 0; i < this->GetNumOrders(); ++i) {
2801 Order* order = this->orders.list->GetOrderAt(i);
2803 bool isRegularOrder = (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
2804 bool isDepotOrder = order->GetType() == OT_GOTO_DEPOT;
2806 if (isRegularOrder && isDepotOrder) {
2807 destination = order->GetDestination();
2808 if (this->type == VEH_AIRCRAFT) {
2809 Station* st = Station::Get(destination);
2810 if (st != NULL && st->airport.HasHangar() && CanVehicleUseStation(this, st)) {
2811 location = st->xy;
2812 foundDepotInOrders = true;
2813 break;
2816 else {
2817 location = Depot::Get(destination)->xy;
2818 reverse = false;
2819 foundDepotInOrders = true;
2820 break;
2825 if (!foundDepotInOrders && !this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2827 if (flags & DC_EXEC) {
2828 if (!(foundDepotInOrders && this->type == VEH_TRAIN)) {
2829 // The OT_LOADING status of trains with depots in their order list will be handled separately in the HandleLoading() method.
2830 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2833 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2834 uint16 &gv_flags = this->GetGroundVehicleFlags();
2835 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2838 if (foundDepotInOrders && this->type == VEH_TRAIN) {
2839 bool service_only = (command & DEPOT_SERVICE) != 0;
2840 SetBit(this->vehicle_flags, service_only ? VF_SHOULD_SERVICE_AT_DEPOT : VF_SHOULD_GOTO_DEPOT);
2841 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2842 } else {
2843 this->dest_tile = location;
2844 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2846 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2847 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2849 /* If there is no depot in front, reverse automatically (trains only) */
2850 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2852 if (this->type == VEH_AIRCRAFT) {
2853 Aircraft *a = Aircraft::From(this);
2854 if (a->state == FLYING && a->targetairport != destination) {
2855 /* The aircraft is now heading for a different hangar than the next in the orders */
2856 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2857 AircraftNextAirportPos_and_Order(a);
2863 return CommandCost();
2868 * Update the cached visual effect.
2869 * @param allow_power_change true if the wagon-is-powered-state may change.
2871 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2873 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2874 const Engine *e = this->GetEngine();
2876 /* Evaluate properties */
2877 byte visual_effect;
2878 switch (e->type) {
2879 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2880 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2881 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2882 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2885 /* Check powered wagon / visual effect callback */
2886 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT) && !HasBit(this->subtype, GVSF_VIRTUAL)) {
2887 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2889 if (callback != CALLBACK_FAILED) {
2890 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2892 callback = GB(callback, 0, 8);
2893 /* Avoid accidentally setting 'visual_effect' to the default value
2894 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2895 if (callback == VE_DEFAULT) {
2896 assert(HasBit(callback, VE_DISABLE_EFFECT));
2897 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2899 visual_effect = callback;
2903 /* Apply default values */
2904 if (visual_effect == VE_DEFAULT ||
2905 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2906 /* Only train engines have default effects.
2907 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2908 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2909 if (visual_effect == VE_DEFAULT) {
2910 visual_effect = 1 << VE_DISABLE_EFFECT;
2911 } else {
2912 SetBit(visual_effect, VE_DISABLE_EFFECT);
2914 } else {
2915 if (visual_effect == VE_DEFAULT) {
2916 /* Also set the offset */
2917 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2919 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2923 this->vcache.cached_vis_effect = visual_effect;
2925 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2926 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2927 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2931 static const int8 _vehicle_smoke_pos[8] = {
2932 1, 1, 1, 0, -1, -1, -1, 0
2936 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2937 * @param v Vehicle to create effects for.
2939 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2941 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2942 if (callback == CALLBACK_FAILED) return;
2944 uint count = GB(callback, 0, 2);
2945 bool auto_center = HasBit(callback, 13);
2946 bool auto_rotate = !HasBit(callback, 14);
2948 int8 l_center = 0;
2949 if (auto_center) {
2950 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2951 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2952 } else {
2953 /* For trains: Compute offset from vehicle position to sprite position */
2954 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2957 Direction l_dir = v->direction;
2958 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2959 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2961 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2962 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2964 for (uint i = 0; i < count; i++) {
2965 uint32 reg = GetRegister(0x100 + i);
2966 uint type = GB(reg, 0, 8);
2967 int8 x = GB(reg, 8, 8);
2968 int8 y = GB(reg, 16, 8);
2969 int8 z = GB(reg, 24, 8);
2971 if (auto_rotate) {
2972 int8 l = x;
2973 int8 t = y;
2974 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2975 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2978 if (type >= 0xF0) {
2979 switch (type) {
2980 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2981 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2982 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2983 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2984 default: break;
2990 uint16 ReversingDistanceTargetSpeed(const Train *v);
2993 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2994 * @pre this->IsPrimaryVehicle()
2996 void Vehicle::ShowVisualEffect() const
2998 assert(this->IsPrimaryVehicle());
2999 bool sound = false;
3001 /* Do not show any smoke when:
3002 * - vehicle smoke is disabled by the player
3003 * - the vehicle is slowing down or stopped (by the player)
3004 * - the vehicle is moving very slowly
3006 if (_settings_game.vehicle.smoke_amount == 0 ||
3007 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
3008 this->cur_speed < 2) {
3009 return;
3012 /* Use the speed as limited by underground and orders. */
3013 uint max_speed = this->GetCurrentMaxSpeed();
3015 if (this->type == VEH_TRAIN) {
3016 const Train *t = Train::From(this);
3017 /* For trains, do not show any smoke when:
3018 * - the train is reversing
3019 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
3020 * - is approaching a reversing point and its speed is equal to maximum approach speed
3022 if (HasBit(t->flags, VRF_REVERSING) ||
3023 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
3024 t->cur_speed >= max_speed) ||
3025 (t->reverse_distance >= 1 && t->cur_speed >= ReversingDistanceTargetSpeed(t))) {
3026 return;
3030 const Vehicle *v = this;
3032 do {
3033 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
3034 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
3035 VisualEffectSpawnModel effect_model = VESM_NONE;
3036 if (advanced) {
3037 effect_offset = VE_OFFSET_CENTRE;
3038 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
3039 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
3040 } else {
3041 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
3042 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
3043 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
3044 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
3045 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
3048 /* Show no smoke when:
3049 * - Smoke has been disabled for this vehicle
3050 * - The vehicle is not visible
3051 * - The vehicle is under a bridge
3052 * - The vehicle is on a depot tile
3053 * - The vehicle is on a tunnel tile
3054 * - The vehicle is a train engine that is currently unpowered */
3055 if (effect_model == VESM_NONE ||
3056 v->vehstatus & VS_HIDDEN ||
3057 IsBridgeAbove(v->tile) ||
3058 IsDepotTile(v->tile) ||
3059 IsTunnelTile(v->tile) ||
3060 (v->type == VEH_TRAIN &&
3061 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
3062 continue;
3065 EffectVehicleType evt = EV_END;
3066 switch (effect_model) {
3067 case VESM_STEAM:
3068 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
3069 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
3070 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
3071 * REGULATION:
3072 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
3073 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
3074 evt = EV_STEAM_SMOKE;
3076 break;
3078 case VESM_DIESEL: {
3079 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
3080 * when smoke emission stops.
3081 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
3082 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
3083 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
3084 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
3085 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
3086 * maximum speed no diesel_smoke is emitted.
3087 * REGULATION:
3088 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
3089 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
3090 int power_weight_effect = 0;
3091 if (v->type == VEH_TRAIN) {
3092 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
3094 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
3095 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
3096 evt = EV_DIESEL_SMOKE;
3098 break;
3101 case VESM_ELECTRIC:
3102 /* Electric train's spark - more often occurs when train is departing (more load)
3103 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
3104 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
3105 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
3106 * REGULATION:
3107 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
3108 if (GB(v->tick_counter, 0, 2) == 0 &&
3109 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
3110 evt = EV_ELECTRIC_SPARK;
3112 break;
3114 default:
3115 NOT_REACHED();
3118 if (evt != EV_END && advanced) {
3119 sound = true;
3120 SpawnAdvancedVisualEffect(v);
3121 } else if (evt != EV_END) {
3122 sound = true;
3124 /* The effect offset is relative to a point 4 units behind the vehicle's
3125 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
3126 * correction factor. */
3127 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
3129 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
3130 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
3132 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
3133 x = -x;
3134 y = -y;
3137 CreateEffectVehicleRel(v, x, y, 10, evt);
3139 } while ((v = v->Next()) != NULL);
3141 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
3145 * Set the next vehicle of this vehicle.
3146 * @param next the next vehicle. NULL removes the next vehicle.
3148 void Vehicle::SetNext(Vehicle *next)
3150 assert(this != next);
3152 if (this->next != NULL) {
3153 /* We had an old next vehicle. Update the first and previous pointers */
3154 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
3155 v->first = this->next;
3157 this->next->previous = NULL;
3160 this->next = next;
3162 if (this->next != NULL) {
3163 /* A new next vehicle. Update the first and previous pointers */
3164 if (this->next->previous != NULL) this->next->previous->next = NULL;
3165 this->next->previous = this;
3166 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
3167 v->first = this->first;
3173 * Adds this vehicle to a shared vehicle chain.
3174 * @param shared_chain a vehicle of the chain with shared vehicles.
3175 * @pre !this->IsOrderListShared()
3177 void Vehicle::AddToShared(Vehicle *shared_chain)
3179 assert(this->previous_shared == NULL && this->next_shared == NULL);
3181 if (shared_chain->orders.list == NULL) {
3182 assert(shared_chain->previous_shared == NULL);
3183 assert(shared_chain->next_shared == NULL);
3184 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
3187 this->next_shared = shared_chain->next_shared;
3188 this->previous_shared = shared_chain;
3190 shared_chain->next_shared = this;
3192 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
3194 shared_chain->orders.list->AddVehicle(this);
3195 shared_chain->orders.list->MarkSeparationInvalid();
3199 * Removes the vehicle from the shared order list.
3201 void Vehicle::RemoveFromShared()
3203 /* Remember if we were first and the old window number before RemoveVehicle()
3204 * as this changes first if needed. */
3205 bool were_first = (this->FirstShared() == this);
3206 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
3208 this->orders.list->MarkSeparationInvalid();
3209 this->orders.list->RemoveVehicle(this);
3211 if (!were_first) {
3212 /* We are not the first shared one, so only relink our previous one. */
3213 this->previous_shared->next_shared = this->NextShared();
3216 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
3219 if (this->orders.list->GetNumVehicles() == 1) {
3220 /* When there is only one vehicle, remove the shared order list window. */
3221 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
3222 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
3223 } else if (were_first) {
3224 /* If we were the first one, update to the new first one.
3225 * Note: FirstShared() is already the new first */
3226 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
3229 this->next_shared = NULL;
3230 this->previous_shared = NULL;
3233 void VehiclesYearlyLoop()
3235 Vehicle *v;
3236 FOR_ALL_VEHICLES(v) {
3237 if (v->IsPrimaryVehicle()) {
3238 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
3239 Money profit = v->GetDisplayProfitThisYear();
3240 if (v->age >= 730 && profit < 0) {
3241 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
3242 SetDParam(0, v->index);
3243 SetDParam(1, profit);
3244 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
3246 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
3249 v->profit_last_year = v->profit_this_year;
3250 v->profit_lifetime += v->profit_this_year;
3251 v->profit_this_year = 0;
3252 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
3255 GroupStatistics::UpdateProfits();
3256 SetWindowClassesDirty(WC_TRAINS_LIST);
3257 SetWindowClassesDirty(WC_SHIPS_LIST);
3258 SetWindowClassesDirty(WC_ROADVEH_LIST);
3259 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
3264 * Can this station be used by the given engine type?
3265 * @param engine_type the type of vehicles to test
3266 * @param st the station to test for
3267 * @return true if and only if the vehicle of the type can use this station.
3268 * @note For road vehicles the Vehicle is needed to determine whether it can
3269 * use the station. This function will return true for road vehicles
3270 * when at least one of the facilities is available.
3272 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
3274 const Engine *e = Engine::GetIfValid(engine_type);
3275 assert(e != NULL);
3277 switch (e->type) {
3278 case VEH_TRAIN:
3279 return (st->facilities & FACIL_TRAIN) != 0;
3281 case VEH_ROAD:
3282 /* For road vehicles we need the vehicle to know whether it can actually
3283 * use the station, but if it doesn't have facilities for RVs it is
3284 * certainly not possible that the station can be used. */
3285 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
3287 case VEH_SHIP:
3288 return (st->facilities & FACIL_DOCK) != 0;
3290 case VEH_AIRCRAFT:
3291 return (st->facilities & FACIL_AIRPORT) != 0 &&
3292 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
3294 default:
3295 return false;
3300 * Can this station be used by the given vehicle?
3301 * @param v the vehicle to test
3302 * @param st the station to test for
3303 * @return true if and only if the vehicle can use this station.
3305 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
3307 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
3309 return CanVehicleUseStation(v->engine_type, st);
3313 * Access the ground vehicle cache of the vehicle.
3314 * @pre The vehicle is a #GroundVehicle.
3315 * @return #GroundVehicleCache of the vehicle.
3317 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
3319 assert(this->IsGroundVehicle());
3320 if (this->type == VEH_TRAIN) {
3321 return &Train::From(this)->gcache;
3322 } else {
3323 return &RoadVehicle::From(this)->gcache;
3328 * Access the ground vehicle cache of the vehicle.
3329 * @pre The vehicle is a #GroundVehicle.
3330 * @return #GroundVehicleCache of the vehicle.
3332 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
3334 assert(this->IsGroundVehicle());
3335 if (this->type == VEH_TRAIN) {
3336 return &Train::From(this)->gcache;
3337 } else {
3338 return &RoadVehicle::From(this)->gcache;
3343 * Access the ground vehicle flags of the vehicle.
3344 * @pre The vehicle is a #GroundVehicle.
3345 * @return #GroundVehicleFlags of the vehicle.
3347 uint16 &Vehicle::GetGroundVehicleFlags()
3349 assert(this->IsGroundVehicle());
3350 if (this->type == VEH_TRAIN) {
3351 return Train::From(this)->gv_flags;
3352 } else {
3353 return RoadVehicle::From(this)->gv_flags;
3358 * Access the ground vehicle flags of the vehicle.
3359 * @pre The vehicle is a #GroundVehicle.
3360 * @return #GroundVehicleFlags of the vehicle.
3362 const uint16 &Vehicle::GetGroundVehicleFlags() const
3364 assert(this->IsGroundVehicle());
3365 if (this->type == VEH_TRAIN) {
3366 return Train::From(this)->gv_flags;
3367 } else {
3368 return RoadVehicle::From(this)->gv_flags;
3373 * Calculates the set of vehicles that will be affected by a given selection.
3374 * @param set [inout] Set of affected vehicles.
3375 * @param v First vehicle of the selection.
3376 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
3377 * @pre \a set must be empty.
3378 * @post \a set will contain the vehicles that will be refitted.
3380 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
3382 if (v->type == VEH_TRAIN) {
3383 Train *u = Train::From(v);
3384 /* Only include whole vehicles, so start with the first articulated part */
3385 u = u->GetFirstEnginePart();
3387 /* Include num_vehicles vehicles, not counting articulated parts */
3388 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
3389 do {
3390 /* Include current vehicle in the selection. */
3391 set.Include(u->index);
3393 /* If the vehicle is multiheaded, add the other part too. */
3394 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
3396 u = u->Next();
3397 } while (u != NULL && u->IsArticulatedPart());