Inline GfxMainBlitterViewport
[openttd/fttd.git] / src / vehicle.cpp
blob42bfd90813c1088ae879c9c9136ccf24cee19e06
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file vehicle.cpp Base implementations of all vehicles. */
12 #include "stdafx.h"
13 #include "error.h"
14 #include "roadveh.h"
15 #include "ship.h"
16 #include "spritecache.h"
17 #include "timetable.h"
18 #include "viewport_func.h"
19 #include "news_func.h"
20 #include "command_func.h"
21 #include "company_func.h"
22 #include "train.h"
23 #include "aircraft.h"
24 #include "newgrf_debug.h"
25 #include "newgrf_sound.h"
26 #include "newgrf_station.h"
27 #include "group_gui.h"
28 #include "strings_func.h"
29 #include "zoom_func.h"
30 #include "date_func.h"
31 #include "vehicle_func.h"
32 #include "autoreplace_func.h"
33 #include "autoreplace_gui.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "depot_func.h"
37 #include "network/network.h"
38 #include "core/pool_func.hpp"
39 #include "economy_base.h"
40 #include "articulated_vehicles.h"
41 #include "roadstop_base.h"
42 #include "core/random_func.hpp"
43 #include "core/backup_type.hpp"
44 #include "order_backup.h"
45 #include "sound_func.h"
46 #include "effectvehicle_func.h"
47 #include "effectvehicle_base.h"
48 #include "vehiclelist.h"
49 #include "map/tunnel.h"
50 #include "map/slope.h"
51 #include "map/bridge.h"
52 #include "gamelog.h"
53 #include "signalbuffer.h"
54 #include "linkgraph/linkgraph.h"
55 #include "linkgraph/refresh.h"
57 #include "table/strings.h"
60 VehicleID _new_vehicle_id;
61 uint16 _returned_refit_capacity; ///< Stores the capacity after a refit operation.
62 uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refit operation (Aircraft only).
65 /** The pool with all our precious vehicles. */
66 template<> Vehicle::Pool Vehicle::PoolItem::pool ("Vehicle");
67 INSTANTIATE_POOL_METHODS(Vehicle)
70 /**
71 * Determine shared bounds of all sprites.
72 * @param [out] bounds Shared bounds.
74 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
76 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
77 for (uint i = 0; i < this->count; ++i) {
78 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
79 if (i == 0) {
80 bounds->left = spr->x_offs;
81 bounds->top = spr->y_offs;
82 bounds->right = spr->width + spr->x_offs - 1;
83 bounds->bottom = spr->height + spr->y_offs - 1;
84 } else {
85 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
86 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
87 int right = spr->width + spr->x_offs - 1;
88 int bottom = spr->height + spr->y_offs - 1;
89 if (right > bounds->right) bounds->right = right;
90 if (bottom > bounds->bottom) bounds->bottom = bottom;
95 /**
96 * Draw the sprite sequence.
97 * @param dpi The area to draw on.
98 * @param x X position
99 * @param y Y position
100 * @param default_pal Vehicle palette
101 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
103 void VehicleSpriteSeq::Draw (BlitArea *dpi, int x, int y, PaletteID default_pal, bool force_pal) const
105 for (uint i = 0; i < this->count; ++i) {
106 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
107 DrawSprite (dpi, this->seq[i].sprite, pal, x, y);
112 * Function to tell if a vehicle needs to be autorenewed
113 * @param *c The vehicle owner
114 * @return true if the vehicle is old enough for replacement
116 bool Vehicle::NeedsAutorenewing (const Company *c) const
118 /* We can always generate the Company pointer when we have the vehicle.
119 * However this takes time and since the Company pointer is often present
120 * when this function is called then it's faster to pass the pointer as an
121 * argument rather than finding it again. */
122 assert(c == Company::Get(this->owner));
124 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
126 /* Only engines need renewing */
127 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
129 return true;
133 * Service a vehicle and all subsequent vehicles in the consist
135 * @param *v The vehicle or vehicle chain being serviced
137 void VehicleServiceInDepot(Vehicle *v)
139 assert(v != NULL);
140 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
142 do {
143 v->date_of_last_service = _date;
144 v->breakdowns_since_last_service = 0;
145 v->reliability = v->GetEngine()->reliability;
146 /* Prevent vehicles from breaking down directly after exiting the depot. */
147 v->breakdown_chance /= 4;
148 v = v->Next();
149 } while (v != NULL && v->HasEngineType());
153 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
155 * @see NeedsAutomaticServicing()
156 * @param scheduled Whether the check is part of a scheduled order.
157 * @return true if the vehicle should go to a depot if a opportunity presents itself.
159 bool Vehicle::NeedsServicing (bool scheduled) const
161 /* Stopped or crashed vehicles will not move, as such making unmovable
162 * vehicles to go for service is lame. */
163 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
165 /* Are we ready for the next service cycle? */
166 const Company *c = Company::Get(this->owner);
167 if (this->ServiceIntervalIsPercent() ?
168 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
169 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
170 return false;
173 /* Service if playing with breakdowns. */
174 if (_settings_game.difficulty.vehicle_breakdowns != 0) return true;
176 /* Check company setting for servicing when breakdowns are disabled. */
177 bool autorenew = c->settings.engine_renew;
178 switch (c->settings.servicing_if_no_breakdowns) {
179 case 0: autorenew &= scheduled; break;
180 case 1: break;
181 default: return true;
184 /* Test whether there is some pending autoreplace.
185 * Note: We do this after the service-interval test.
186 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
187 bool pending_replace = false;
188 Money needed_money = c->settings.engine_renew_money;
189 if (needed_money > c->money) return false;
191 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
192 bool replace_when_old = false;
193 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
195 if (new_engine == INVALID_ENGINE) {
196 if (!autorenew) continue;
197 new_engine = v->engine_type;
198 replace_when_old = true;
201 /* Check engine availability */
202 if (!HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
203 /* Is the vehicle old if we are not always replacing? */
204 if (replace_when_old && !v->NeedsAutorenewing(c)) continue;
206 /* Check refittability */
207 uint32 available_cargo_types, union_mask;
208 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
209 /* Is there anything to refit? */
210 if (union_mask != 0) {
211 CargoID cargo_type;
212 /* We cannot refit to mixed cargoes in an automated way */
213 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
215 /* Did the old vehicle carry anything? */
216 if (cargo_type != CT_INVALID) {
217 /* We can't refit the vehicle to carry the cargo we want */
218 if (!HasBit(available_cargo_types, cargo_type)) continue;
222 /* Check money.
223 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
224 pending_replace = true;
225 needed_money += 2 * Engine::Get(new_engine)->GetCost();
226 if (needed_money > c->money) return false;
229 return pending_replace;
233 * Checks if the current order should be interrupted for a service-in-depot order.
234 * @see NeedsServicing()
235 * @return true if the current order should be interrupted.
237 bool Vehicle::NeedsAutomaticServicing() const
239 if (this->HasDepotOrder()) return false;
240 if (this->current_order.IsType(OT_LOADING)) return false;
241 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
242 return this->NeedsServicing (false);
245 uint Vehicle::Crash(bool flooded)
247 assert((this->vehstatus & VS_CRASHED) == 0);
248 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
250 uint pass = 0;
251 /* Stop the vehicle. */
252 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
253 /* crash all wagons, and count passengers */
254 for (Vehicle *v = this; v != NULL; v = v->Next()) {
255 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
256 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
257 v->vehstatus |= VS_CRASHED;
258 v->MarkAllViewportsDirty();
261 /* Dirty some windows */
262 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
263 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
264 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
265 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
267 delete this->cargo_payment;
268 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
270 return RandomRange(pass + 1); // Randomise deceased passengers.
275 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
276 * @param engine The engine that caused the problem
277 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
278 * @param part2 Part 2 of the error message, taking the engine as parameter 2
279 * @param bug_type Flag to check and set in grfconfig
280 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
282 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
284 const Engine *e = Engine::Get(engine);
285 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
287 /* Missing GRF. Nothing useful can be done in this situation. */
288 if (grfconfig == NULL) return;
290 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
291 SetBit(grfconfig->grf_bugs, bug_type);
292 SetDParamStr(0, grfconfig->GetName());
293 SetDParam(1, engine);
294 ShowErrorMessage(part1, part2, WL_CRITICAL);
295 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
298 /* debug output */
299 char buffer[512];
301 SetDParamStr(0, grfconfig->GetName());
302 GetString (buffer, part1);
303 DEBUG(grf, 0, "%s", buffer + 3);
305 SetDParam(1, engine);
306 GetString (buffer, part2);
307 DEBUG(grf, 0, "%s", buffer + 3);
311 * Logs a bug in GRF and shows a warning message if this
312 * is for the first time this happened.
313 * @param u first vehicle of chain
315 void VehicleLengthChanged(const Vehicle *u)
317 /* show a warning once for each engine in whole game and once for each GRF after each game load */
318 const Engine *engine = u->GetEngine();
319 uint32 grfid = engine->grf_prop.grffile->grfid;
320 GRFConfig *grfconfig = GetGRFConfig(grfid);
321 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
322 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
327 * Vehicle constructor.
328 * @param type Type of the new vehicle.
330 Vehicle::Vehicle(VehicleType type)
332 this->type = type;
333 this->coord.left = INVALID_COORD;
334 this->group_id = DEFAULT_GROUP;
335 this->fill_percent_te_id = INVALID_TE_ID;
336 this->first = this;
337 this->colourmap = PAL_NONE;
338 this->cargo_age_counter = 1;
339 this->last_station_visited = INVALID_STATION;
340 this->last_loading_station = INVALID_STATION;
344 * Get a value for a vehicle's random_bits.
345 * @return A random value from 0 to 255.
347 byte VehicleRandomBits()
349 return GB(Random(), 0, 8);
354 * Vehicle hash update template function
355 * @param v The vehicle to update
356 * @param link The vehicle link to update within Vehicle
357 * @param old_hash The hash to remove the vehicle from, if any
358 * @param new_hash The hash to add the vehicle to, if any
360 static void UpdateVehicleHash(Vehicle *v, VehicleHashLink Vehicle::*link, Vehicle **old_hash, Vehicle **new_hash)
362 if (old_hash == new_hash) return;
364 /* Remove from the old position in the hash table */
365 if (old_hash != NULL) {
366 if ((v->*link).next != NULL) ((v->*link).next->*link).pprev = (v->*link).pprev;
367 *(v->*link).pprev = (v->*link).next;
370 /* Insert vehicle at beginning of the new position in the hash table */
371 if (new_hash != NULL) {
372 (v->*link).next = *new_hash;
373 if ((v->*link).next != NULL) ((v->*link).next->*link).pprev = &(v->*link).next;
374 (v->*link).pprev = new_hash;
375 *new_hash = v;
379 /* Forward declaration for HashAreaIterator */
380 template <uint nx, uint ny>
381 struct HashAreaIterator;
384 * Hash pack template class
385 * @tparam nx Number of bits to use for x.
386 * @tparam ny Number of bits to use for y.
388 template <uint nx, uint ny>
389 struct HashPack {
390 static const uint PACK_BIT0_X = 0;
391 static const uint PACK_BITS_X = nx;
392 static const uint PACK_MASK_X = ((1 << PACK_BITS_X) - 1) << PACK_BIT0_X;
394 static const uint PACK_BIT0_Y = PACK_BIT0_X + PACK_BITS_X;
395 static const uint PACK_BITS_Y = ny;
396 static const uint PACK_MASK_Y = ((1 << PACK_BITS_Y) - 1) << PACK_BIT0_Y;
398 static const uint PACK_BITS = PACK_BIT0_Y + PACK_BITS_Y;
399 static const uint PACK_SIZE = 1 << PACK_BITS;
401 typedef HashAreaIterator <nx, ny> BaseAreaIterator;
403 static inline uint pack_x (uint x)
405 return (x << PACK_BIT0_X) & PACK_MASK_X;
408 static inline uint pack_y (uint y)
410 return (y << PACK_BIT0_Y) & PACK_MASK_Y;
413 static inline uint pack (uint x, uint y)
415 return pack_x(x) + pack_y(y);
418 static inline uint next_x (uint x)
420 return (x + (1 << PACK_BIT0_X)) & PACK_MASK_X;
423 static inline uint next_y (uint y)
425 return (y + (1 << PACK_BIT0_Y)) & PACK_MASK_Y;
430 * Area iterator for a hash class
431 * @tparam nx Number of bits to use for x.
432 * @tparam ny Number of bits to use for y.
434 template <uint nx, uint ny>
435 struct HashAreaIterator : HashPack <nx, ny> {
436 uint x0, x1, y0, y1;
437 uint x, y;
439 void reset (uint xx0, uint xx1, uint yy0, uint yy1)
441 x0 = this->pack_x (xx0);
442 x1 = this->pack_x (xx1);
444 y0 = this->pack_y (yy0);
445 y1 = this->pack_y (yy1);
447 x = x0;
448 y = y0;
451 uint get() const
453 return x + y;
456 bool next()
458 if (x != x1) {
459 x = this->next_x(x);
460 return true;
461 } else if (y != y1) {
462 y = this->next_y(y);
463 x = x0;
464 return true;
465 } else {
466 return false;
472 struct VehicleTileHash : HashPack <7, 7> {
473 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
474 * lookup times at the expense of memory usage. */
476 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
477 * Profiling results show that 0 is fastest. */
478 static const uint HASH_RES = 0;
480 static const uint HASH_OFFSET_X = HASH_RES;
481 static const uint HASH_BITS_X = PACK_BITS_X;
483 static const uint HASH_OFFSET_Y = HASH_RES;
484 static const uint HASH_BITS_Y = PACK_BITS_Y;
486 static const uint HASH_SIZE = PACK_SIZE;
488 Vehicle *buckets[HASH_SIZE];
490 static inline uint hash (int x, int y)
492 return pack (GB(x, HASH_OFFSET_X, HASH_BITS_X), GB(y, HASH_OFFSET_Y, HASH_BITS_Y));
495 static inline uint hash (TileIndex tile)
497 return hash (TileX(tile), TileY(tile));
500 inline Vehicle **get_bucket (TileIndex tile)
502 return &this->buckets[hash(tile)];
505 void update (Vehicle *v, bool remove = false)
507 Vehicle **new_hash = remove ? NULL : get_bucket(v->tile);
509 UpdateVehicleHash(v, &Vehicle::hash_tile_link,
510 v->hash_tile_current, new_hash);
512 /* Remember current hash position */
513 v->hash_tile_current = new_hash;
516 void reset()
518 Vehicle *v;
519 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
520 memset (this->buckets, 0, sizeof(this->buckets));
523 struct AreaIterator : BaseAreaIterator {
524 AreaIterator (int xl, int xu, int yl, int yu)
526 this->reset (GB(xl, HASH_OFFSET_X, HASH_BITS_X), GB(xu, HASH_OFFSET_X, HASH_BITS_X),
527 GB(yl, HASH_OFFSET_Y, HASH_BITS_Y), GB(yu, HASH_OFFSET_Y, HASH_BITS_Y));
532 static VehicleTileHash vehicle_tile_hash;
535 * Get the first vehicle for a VehicleTileIterator
536 * @param tile The tile to iterate at
538 Vehicle *VehicleTileIterator::first (TileIndex tile)
540 return *vehicle_tile_hash.get_bucket(tile);
545 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
546 * @note Do not call this function directly!
547 * @param x The X location on the map
548 * @param y The Y location on the map
549 * @param data Arbitrary data passed to proc
550 * @param proc The proc that determines whether a vehicle will be "found".
551 * @param find_first Whether to return on the first found or iterate over
552 * all vehicles
553 * @return the best matching or first vehicle (depending on find_first).
555 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
557 const int COLL_DIST = 6;
559 /* Hash area to scan is from xl,yl to xu,yu */
560 VehicleTileHash::AreaIterator iter (
561 (x - COLL_DIST) / TILE_SIZE, (x + COLL_DIST) / TILE_SIZE,
562 (y - COLL_DIST) / TILE_SIZE, (y + COLL_DIST) / TILE_SIZE);
564 do {
565 Vehicle *v = vehicle_tile_hash.buckets[iter.get()];
566 for (; v != NULL; v = v->hash_tile_link.next) {
567 Vehicle *a = proc(v, data);
568 if (find_first && a != NULL) return a;
570 } while (iter.next());
572 return NULL;
576 * Find a vehicle from a specific location. It will call proc for ALL vehicles
577 * on the tile and YOU must make SURE that the "best one" is stored in the
578 * data value and is ALWAYS the same regardless of the order of the vehicles
579 * where proc was called on!
580 * When you fail to do this properly you create an almost untraceable DESYNC!
581 * @note The return value of proc will be ignored.
582 * @note Use this when you have the intention that all vehicles
583 * should be iterated over.
584 * @param x The X location on the map
585 * @param y The Y location on the map
586 * @param data Arbitrary data passed to proc
587 * @param proc The proc that determines whether a vehicle will be "found".
589 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
591 VehicleFromPosXY(x, y, data, proc, false);
595 * Checks whether a vehicle in on a specific location. It will call proc for
596 * vehicles until it returns non-NULL.
597 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
598 * should be iterated over.
599 * @param x The X location on the map
600 * @param y The Y location on the map
601 * @param data Arbitrary data passed to proc
602 * @param proc The proc that determines whether a vehicle will be "found".
603 * @return True if proc returned non-NULL.
605 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
607 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
611 * Ensure there is no vehicle at the ground at the given position.
612 * @param tile Position to examine.
613 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
615 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
617 int z = GetTileMaxPixelZ(tile);
619 VehicleTileFinder iter (tile);
620 Vehicle *v = NULL;
621 while (!iter.finished()) {
622 v = iter.next();
624 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) continue;
625 if (v->z_pos > z) continue;
627 iter.set_found();
630 /* Value v is not safe in MP games, however, it is used to generate a local
631 * error message only (which may be different for different machines).
632 * Such a message does not affect MP synchronisation.
634 if (iter.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
635 return CommandCost();
639 * Finds vehicle in tunnel / bridge
640 * @param tile first end
641 * @param endtile second end
642 * @param ignore Ignore this vehicle when searching
643 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
645 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
647 /* Value v is not safe in MP games, however, it is used to generate a local
648 * error message only (which may be different for different machines).
649 * Such a message does not affect MP synchronisation.
651 Vehicle *v = NULL;
653 VehicleTileFinder iter1 (tile);
654 while (!iter1.finished()) {
655 v = iter1.next();
657 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) continue;
658 if (v == ignore) continue;
660 iter1.set_found();
662 if (iter1.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
664 VehicleTileFinder iter2 (endtile);
665 while (!iter2.finished()) {
666 v = iter2.next();
668 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) continue;
669 if (v == ignore) continue;
671 iter2.set_found();
673 if (iter2.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
675 return CommandCost();
678 static Track AllowedNonOverlappingTrack(TrackBits bits)
680 switch (bits) {
681 case TRACK_BIT_UPPER: return TRACK_LOWER;
682 case TRACK_BIT_LOWER: return TRACK_UPPER;
683 case TRACK_BIT_LEFT: return TRACK_RIGHT;
684 case TRACK_BIT_RIGHT: return TRACK_LEFT;
685 default: return INVALID_TRACK;
690 * Tests if a vehicle interacts with the specified track bits.
691 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
693 * @param tile The tile.
694 * @param track_bits The track bits.
695 * @return \c true if no train that interacts, is found. \c false if a train is found.
697 bool CheckTrackBitsFree (TileIndex tile, TrackBits track_bits)
699 assert(track_bits != TRACK_BIT_NONE);
701 Track allowed = AllowedNonOverlappingTrack(track_bits);
703 VehicleTileFinder iter (tile);
704 while (!iter.finished()) {
705 Vehicle *v = iter.next();
706 if (v->type != VEH_TRAIN) continue;
708 Trackdir trackdir = Train::From(v)->trackdir;
709 if (trackdir >= TRACKDIR_END) continue; // in wormhole or depot
711 if (TrackdirToTrack(trackdir) != allowed) iter.set_found();
714 return !iter.was_found();
718 * Check if there is a train that interacts with the specified track bits of a
719 * rail bridge head or is on the bridge middle part heading towards this end.
720 * Note that trains on the middle part of a bridge can have either bridge head
721 * as their tile.
722 * @param tile Bridge head tile.
723 * @param bits Track bits to check for.
724 * @return Whether there is a train on the bridge tracks or middle part.
726 bool CheckBridgeEndTrackBitsFree (TileIndex tile, TrackBits bits)
728 assert(bits != TRACK_BIT_NONE);
730 Track allowed = AllowedNonOverlappingTrack(bits);
731 VehicleTileFinder iter (tile);
732 while (!iter.finished()) {
733 Vehicle *v = iter.next();
734 if (v->type != VEH_TRAIN) continue;
736 Trackdir trackdir = Train::From(v)->trackdir;
737 if (TrackdirToTrack(trackdir) != allowed) iter.set_found();
740 return !iter.was_found();
743 static bool CheckTunnelBridgeEndMiddleFree (TileIndex tile)
745 VehicleTileFinder iter (tile);
746 while (!iter.finished()) {
747 Vehicle *v = iter.next();
748 if (v->type == VEH_TRAIN && Train::From(v)->trackdir == TRACKDIR_WORMHOLE) iter.set_found();
750 return !iter.was_found();
754 * Tests if there is a train on the middle bridge part
755 * @param tile1 one bridge end
756 * @param tile2 the other bridge end
757 * @return whether there is a train on the bridge
759 bool CheckTunnelBridgeMiddleFree (TileIndex tile1, TileIndex tile2)
761 return CheckTunnelBridgeEndMiddleFree (tile1) &&
762 CheckTunnelBridgeEndMiddleFree (tile2);
766 struct VehicleViewportHash : HashPack <6, 6> {
767 static const uint HASH_OFFSET_X = 7 + ZOOM_LVL_SHIFT;
768 static const uint HASH_BITS_X = PACK_BITS_X;
770 static const uint HASH_OFFSET_Y = 6 + ZOOM_LVL_SHIFT;
771 static const uint HASH_BITS_Y = PACK_BITS_Y;
773 static const uint HASH_SIZE = PACK_SIZE;
775 Vehicle *buckets[HASH_SIZE];
777 static inline uint hash (int x, int y)
779 return pack (GB(x, HASH_OFFSET_X, HASH_BITS_X), GB(y, HASH_OFFSET_Y, HASH_BITS_Y));
782 inline Vehicle **get_bucket (int x, int y)
784 return (x == INVALID_COORD) ? NULL : &buckets[hash(x, y)];
787 void update (Vehicle *v, int x, int y)
789 UpdateVehicleHash(v, &Vehicle::hash_viewport_link,
790 this->get_bucket (v->coord.left, v->coord.top),
791 this->get_bucket (x, y));
794 void reset()
796 memset (this->buckets, 0, sizeof(this->buckets));
799 struct AreaIterator : BaseAreaIterator {
800 AreaIterator (int l, uint w, int t, uint h)
802 uint x0, x1, y0, y1;
804 if (w < (1 << (HASH_OFFSET_X + HASH_BITS_X))) {
805 x0 = GB(l, HASH_OFFSET_X, HASH_BITS_X);
806 x1 = GB(l + w, HASH_OFFSET_X, HASH_BITS_X);
807 } else {
808 /* scan whole hash row */
809 x0 = 0;
810 x1 = (1 << HASH_BITS_X) - 1;
813 if (h < (1 << (HASH_OFFSET_Y + HASH_BITS_Y))) {
814 y0 = GB(t, HASH_OFFSET_Y, HASH_BITS_Y);
815 y1 = GB(t + h, HASH_OFFSET_Y, HASH_BITS_Y);
816 } else {
817 /* scan whole column */
818 y0 = 0;
819 y1 = (1 << HASH_BITS_Y) - 1;
822 this->reset (x0, x1, y0, y1);
827 static VehicleViewportHash vehicle_viewport_hash;
829 void ResetVehicleHash()
831 vehicle_viewport_hash.reset();
832 vehicle_tile_hash.reset();
835 void ResetVehicleColourMap()
837 Vehicle *v;
838 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
842 * List of vehicles that should check for autoreplace this tick.
843 * Mapping of vehicle -> leave depot immediately after autoreplace.
845 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
846 static AutoreplaceMap _vehicles_to_autoreplace;
848 void InitializeVehicles()
850 _vehicles_to_autoreplace.Reset();
851 ResetVehicleHash();
854 uint CountVehiclesInChain(const Vehicle *v)
856 uint count = 0;
857 do count++; while ((v = v->Next()) != NULL);
858 return count;
862 * Check if a vehicle is counted in num_engines in each company struct
863 * @return true if the vehicle is counted in num_engines
865 bool Vehicle::IsEngineCountable() const
867 switch (this->type) {
868 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
869 case VEH_TRAIN:
870 return !this->IsArticulatedPart() && // tenders and other articulated parts
871 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
872 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
873 case VEH_SHIP: return true;
874 default: return false; // Only count company buildable vehicles
879 * Check whether Vehicle::engine_type has any meaning.
880 * @return true if the vehicle has a useable engine type.
882 bool Vehicle::HasEngineType() const
884 switch (this->type) {
885 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
886 case VEH_TRAIN:
887 case VEH_ROAD:
888 case VEH_SHIP: return true;
889 default: return false;
894 * Retrieves the engine of the vehicle.
895 * @return Engine of the vehicle.
896 * @pre HasEngineType() == true
898 const Engine *Vehicle::GetEngine() const
900 return Engine::Get(this->engine_type);
904 * Retrieve the NewGRF the vehicle is tied to.
905 * This is the GRF providing the Action 3 for the engine type.
906 * @return NewGRF associated to the vehicle.
908 const GRFFile *Vehicle::GetGRF() const
910 return this->GetEngine()->GetGRF();
914 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
915 * This is the GRF providing the Action 3 for the engine type.
916 * @return GRF ID of the associated NewGRF.
918 uint32 Vehicle::GetGRFID() const
920 return this->GetEngine()->GetGRFID();
924 * Handle the pathfinding result, especially the lost status.
925 * If the vehicle is now lost and wasn't previously fire an
926 * event to the AIs and a news message to the user. If the
927 * vehicle is not lost anymore remove the news message.
928 * @param path_found Whether the vehicle has a path to its destination.
930 void Vehicle::HandlePathfindingResult(bool path_found)
932 if (path_found) {
933 /* Route found, is the vehicle marked with "lost" flag? */
934 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
936 /* Clear the flag as the PF's problem was solved. */
937 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
938 /* Delete the news item. */
939 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
940 return;
943 /* Were we already lost? */
944 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
946 /* It is first time the problem occurred, set the "lost" flag. */
947 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
948 /* Notify user about the event. */
949 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
950 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
951 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_VEHICLE_IS_LOST, this->index);
955 /** Destroy all stuff that (still) needs the virtual functions to work properly */
956 void Vehicle::PreDestructor()
958 if (CleaningPool()) return;
960 if (Station::IsValidID(this->last_station_visited)) {
961 Station *st = Station::Get(this->last_station_visited);
962 st->loading_vehicles.remove(this);
964 HideFillingPercent(&this->fill_percent_te_id);
965 this->CancelReservation (st);
966 delete this->cargo_payment;
967 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
970 if (this->IsEngineCountable()) {
971 GroupStatistics::CountEngine(this, -1);
972 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
973 GroupStatistics::UpdateAutoreplace(this->owner);
975 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
976 DeleteGroupHighlightOfVehicle(this);
979 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
980 Aircraft *a = Aircraft::From(this);
981 Station *st = GetTargetAirportIfValid(a);
982 if (st != NULL) {
983 const AirportFTA *layout = st->airport.GetFTA()->layout;
984 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
989 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
990 RoadVehicle *v = RoadVehicle::From(this);
991 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
992 /* Leave the drive through roadstop, when you have not already left it. */
993 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->LeaveDriveThrough (TrackdirToExitdir((Trackdir)(v->state & RVSB_ROAD_STOP_TRACKDIR_MASK)), v->gcache.cached_total_length);
997 if (this->Previous() == NULL) {
998 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
1001 if (this->IsPrimaryVehicle()) {
1002 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
1003 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
1004 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
1005 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
1006 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
1007 SetWindowDirty(WC_COMPANY, this->owner);
1008 OrderBackup::ClearVehicle(this);
1010 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
1012 this->cargo.Truncate();
1013 DeleteVehicleOrders(this);
1014 DeleteDepotHighlightOfVehicle(this);
1016 extern void StopGlobalFollowVehicle(const Vehicle *v);
1017 StopGlobalFollowVehicle(this);
1019 ReleaseDisastersTargetingVehicle(this->index);
1022 Vehicle::~Vehicle()
1024 if (CleaningPool()) {
1025 this->cargo.OnCleanPool();
1026 return;
1029 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
1030 * it may happen that vehicle chain is deleted when visible */
1031 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
1033 Vehicle *v = this->Next();
1034 this->SetNext(NULL);
1036 delete v;
1038 vehicle_tile_hash.update (this, true);
1039 vehicle_viewport_hash.update (this, INVALID_COORD, 0);
1040 DeleteVehicleNews(this->index, INVALID_STRING_ID);
1041 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
1045 * Adds a vehicle to the list of vehicles that visited a depot this tick
1046 * @param *v vehicle to add
1048 void VehicleEnteredDepotThisTick(Vehicle *v)
1050 /* Vehicle should stop in the depot if it was in 'stopping' state */
1051 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
1053 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
1054 * stopping in the depot, so we stop it to ensure that it will not reserve
1055 * the path out of the depot before we might autoreplace it to a different
1056 * engine. The new engine would not own the reserved path we store that we
1057 * stopped the vehicle, so autoreplace can start it again */
1058 v->vehstatus |= VS_STOPPED;
1062 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
1063 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
1064 * so each day, all vehicles are processes in DAY_TICKS steps.
1066 static void RunVehicleDayProc()
1068 if (_game_mode != GM_NORMAL) return;
1070 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1071 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
1072 Vehicle *v = Vehicle::Get(i);
1073 if (v == NULL) continue;
1075 /* Call the 32-day callback if needed */
1076 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
1077 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
1078 if (callback != CALLBACK_FAILED) {
1079 if (HasBit(callback, 0)) {
1080 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
1083 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1084 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1085 if (callback != 0) v->First()->MarkDirty();
1087 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
1091 /* This is called once per day for each vehicle, but not in the first tick of the day */
1092 v->OnNewDay();
1096 void CallVehicleTicks()
1098 _vehicles_to_autoreplace.Clear();
1100 RunVehicleDayProc();
1102 Station *st;
1103 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
1105 Vehicle *v;
1106 FOR_ALL_VEHICLES(v) {
1107 /* Vehicle could be deleted in this tick */
1108 if (!v->Tick()) {
1109 assert(Vehicle::Get(vehicle_index) == NULL);
1110 continue;
1113 assert(Vehicle::Get(vehicle_index) == v);
1115 switch (v->type) {
1116 default: break;
1118 case VEH_TRAIN:
1119 case VEH_ROAD:
1120 case VEH_AIRCRAFT:
1121 case VEH_SHIP: {
1122 Vehicle *front = v->First();
1124 if (v->vcache.cached_cargo_age_period != 0) {
1125 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
1126 if (--v->cargo_age_counter == 0) {
1127 v->cargo.AgeCargo();
1128 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
1132 /* Do not play any sound when crashed */
1133 if (front->vehstatus & VS_CRASHED) continue;
1135 /* Do not play any sound when in depot or tunnel */
1136 if (v->vehstatus & VS_HIDDEN) continue;
1138 /* Do not play any sound when stopped */
1139 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1141 /* Check vehicle type specifics */
1142 switch (v->type) {
1143 case VEH_TRAIN:
1144 if (Train::From(v)->IsWagon()) continue;
1145 break;
1147 case VEH_ROAD:
1148 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1149 break;
1151 case VEH_AIRCRAFT:
1152 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1153 break;
1155 default:
1156 break;
1159 v->motion_counter += front->cur_speed;
1160 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1161 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1163 /* Play an alternating running sound every 16 ticks */
1164 if (GB(v->tick_counter, 0, 4) == 0) {
1165 /* Play running sound when speed > 0 and not braking */
1166 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1167 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1170 break;
1175 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1176 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1177 v = it->first;
1178 /* Autoreplace needs the current company set as the vehicle owner */
1179 cur_company.Change(v->owner);
1181 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1182 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1183 * they are already leaving the depot again before being replaced. */
1184 if (it->second) v->vehstatus &= ~VS_STOPPED;
1186 /* Store the position of the effect as the vehicle pointer will become invalid later */
1187 int x = v->x_pos;
1188 int y = v->y_pos;
1189 int z = v->z_pos;
1191 const Company *c = Company::Get(_current_company);
1192 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1193 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1194 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1196 if (!IsLocalCompany()) continue;
1198 if (res.Succeeded()) {
1199 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1200 continue;
1203 StringID error_message = res.GetErrorMessage();
1204 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1206 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1208 StringID message;
1209 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1210 message = error_message;
1211 } else {
1212 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1215 AddNewsItem<VehicleAdviceNewsItem> (message, v->index, error_message);
1218 cur_company.Restore();
1222 * Add vehicle sprite for drawing to the screen.
1223 * @param vd Viewport drawer to use.
1224 * @param v Vehicle to draw.
1226 static void DoDrawVehicle (ViewportDrawer *vd, const Vehicle *v)
1228 PaletteID pal = PAL_NONE;
1230 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1232 /* Check whether the vehicle shall be transparent due to the game state */
1233 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1235 if (v->type == VEH_EFFECT) {
1236 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1237 * However, transparent smoke and bubbles look weird, so always hide them. */
1238 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1239 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1242 StartSpriteCombine (vd);
1243 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1244 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1245 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1246 AddSortableSpriteToDraw (vd, v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1247 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1249 EndSpriteCombine (vd);
1253 * Add the vehicle sprites that should be drawn at a part of the screen.
1254 * @param dpi Rectangle being drawn.
1256 void ViewportAddVehicles (ViewportDrawer *vd, const DrawPixelInfo *dpi)
1258 /* The bounding rectangle */
1259 const int l = dpi->left;
1260 const int r = dpi->left + dpi->width;
1261 const int t = dpi->top;
1262 const int b = dpi->top + dpi->height;
1264 /* The hash area to scan */
1265 VehicleViewportHash::AreaIterator iter (
1266 l - (70 * ZOOM_LVL_BASE), dpi->width + (70 * ZOOM_LVL_BASE),
1267 t - (70 * ZOOM_LVL_BASE), dpi->height + (70 * ZOOM_LVL_BASE));
1269 do {
1270 const Vehicle *v = vehicle_viewport_hash.buckets[iter.get()];
1272 while (v != NULL) {
1273 if (!(v->vehstatus & VS_HIDDEN) &&
1274 l <= v->coord.right &&
1275 t <= v->coord.bottom &&
1276 r >= v->coord.left &&
1277 b >= v->coord.top) {
1278 DoDrawVehicle (vd, v);
1280 v = v->hash_viewport_link.next;
1283 } while (iter.next());
1287 * Find the vehicle close to the clicked coordinates.
1288 * @param x X coordinate in the viewport.
1289 * @param y Y coordinate in the viewport.
1290 * @return Closest vehicle, or \c NULL if none found.
1292 Vehicle *CheckClickOnVehicle (int x, int y)
1294 Vehicle *found = NULL, *v;
1295 uint dist, best_dist = UINT_MAX;
1297 FOR_ALL_VEHICLES(v) {
1298 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1299 x >= v->coord.left && x <= v->coord.right &&
1300 y >= v->coord.top && y <= v->coord.bottom) {
1302 dist = max(
1303 abs(((v->coord.left + v->coord.right) >> 1) - x),
1304 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1307 if (dist < best_dist) {
1308 found = v;
1309 best_dist = dist;
1314 return found;
1318 * Decrease the value of a vehicle.
1319 * @param v %Vehicle to devaluate.
1321 void DecreaseVehicleValue(Vehicle *v)
1323 v->value -= v->value >> 8;
1324 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1327 static const byte _breakdown_chance[64] = {
1328 3, 3, 3, 3, 3, 3, 3, 3,
1329 4, 4, 5, 5, 6, 6, 7, 7,
1330 8, 8, 9, 9, 10, 10, 11, 11,
1331 12, 13, 13, 13, 13, 14, 15, 16,
1332 17, 19, 21, 25, 28, 31, 34, 37,
1333 40, 44, 48, 52, 56, 60, 64, 68,
1334 72, 80, 90, 100, 110, 120, 130, 140,
1335 150, 170, 190, 210, 230, 250, 250, 250,
1338 void CheckVehicleBreakdown(Vehicle *v)
1340 int rel, rel_old;
1342 /* decrease reliability */
1343 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1344 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1346 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1347 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1348 v->cur_speed < 5 || _game_mode == GM_MENU) {
1349 return;
1352 uint32 r = Random();
1354 /* increase chance of failure */
1355 int chance = v->breakdown_chance + 1;
1356 if (Chance16I(1, 25, r)) chance += 25;
1357 v->breakdown_chance = min(255, chance);
1359 /* calculate reliability value to use in comparison */
1360 rel = v->reliability;
1361 if (v->type == VEH_SHIP) rel += 0x6666;
1363 /* reduced breakdowns? */
1364 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1366 /* check if to break down */
1367 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1368 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1369 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1370 v->breakdown_chance = 0;
1375 * Handle all of the aspects of a vehicle breakdown
1376 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1377 * @return true iff the vehicle is stopped because of a breakdown
1378 * @note This function always returns false for aircraft, since these never stop for breakdowns
1380 bool Vehicle::HandleBreakdown()
1382 /* Possible states for Vehicle::breakdown_ctr
1383 * 0 - vehicle is running normally
1384 * 1 - vehicle is currently broken down
1385 * 2 - vehicle is going to break down now
1386 * >2 - vehicle is counting down to the actual breakdown event */
1387 switch (this->breakdown_ctr) {
1388 case 0:
1389 return false;
1391 case 2:
1392 this->breakdown_ctr = 1;
1394 if (this->breakdowns_since_last_service != 255) {
1395 this->breakdowns_since_last_service++;
1398 if (this->type == VEH_AIRCRAFT) {
1399 /* Aircraft just need this flag, the rest is handled elsewhere */
1400 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1401 } else {
1402 this->cur_speed = 0;
1404 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1405 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1406 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1407 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1408 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1411 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1412 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1413 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1417 this->MarkDirty(); // Update graphics after speed is zeroed
1418 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1419 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1421 /* FALL THROUGH */
1422 case 1:
1423 /* Aircraft breakdowns end only when arriving at the airport */
1424 if (this->type == VEH_AIRCRAFT) return false;
1426 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1427 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1428 if (--this->breakdown_delay == 0) {
1429 this->breakdown_ctr = 0;
1430 this->MarkDirty();
1431 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1434 return true;
1436 default:
1437 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1438 return false;
1443 * Update age of a vehicle.
1444 * @param v Vehicle to update.
1446 void AgeVehicle(Vehicle *v)
1448 if (v->age < MAX_DAY) {
1449 v->age++;
1450 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1453 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1455 int age = v->age - v->max_age;
1456 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1457 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1458 v->reliability_spd_dec <<= 1;
1461 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1463 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1464 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1466 /* Don't warn if a renew is active */
1467 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1469 StringID str;
1470 if (age == -DAYS_IN_LEAP_YEAR) {
1471 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1472 } else if (age == 0) {
1473 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1474 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1475 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1476 } else {
1477 return;
1480 AddNewsItem<VehicleAdviceNewsItem> (str, v->index);
1484 * Calculates how full a vehicle is.
1485 * @param front The front vehicle of the consist to check.
1486 * @param colour The string to show depending on if we are unloading or loading
1487 * @return A percentage of how full the Vehicle is.
1488 * Rounding is done in such a way that 0% and 100% are only returned
1489 * if the vehicle is completely empty or full, respectively.
1490 * This is useful for both display and conditional orders.
1492 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1494 bool is_loading = front->current_order.IsType(OT_LOADING);
1496 /* The station may be NULL when the (colour) string does not need to be set. */
1497 const Station *st = Station::GetIfValid(front->last_station_visited);
1498 assert(colour == NULL || (st != NULL && is_loading));
1500 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1501 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1503 /* Count up max and used */
1504 uint count = 0; // Total cargo count
1505 uint max = 0; // Total capacity
1506 bool loading = false; // Any vehicle is loading
1507 bool unloading = false; // Any vehicle is unloading
1508 bool all_unloading = true; // All vehicles are unloading, if any is
1509 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1510 count += v->cargo.StoredCount();
1511 max += v->cargo_cap;
1512 if (v->cargo_cap != 0 && colour != NULL) {
1513 if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
1514 unloading = true;
1515 } else {
1516 all_unloading = false;
1519 loading |= !order_no_load &&
1520 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1521 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1525 if (colour != NULL) {
1526 if (!unloading) {
1527 *colour = loading ? STR_PERCENT_UP : STR_PERCENT_NONE;
1528 } else if (all_unloading || !loading) {
1529 *colour = STR_PERCENT_DOWN;
1530 } else {
1531 *colour = STR_PERCENT_UP_DOWN;
1535 /* Train without capacity */
1536 if (max == 0) return 100;
1538 /* Compute and return the percentage */
1540 /* Correction to use for the percentage.
1541 * It is an affine, decreasing function on count that satisfies:
1542 * k == max - 1 if count == 0
1543 * k == 0 if count == max
1544 * max - count - 1 <= k <= max - count
1546 uint k = ((max - count) * (max - 1)) / max;
1548 /* Percentage to return.
1549 * It is an affine, increasing function on count that satisfies:
1550 * p == 0 iff count == 0
1551 * p == 100 iff count == max
1553 return (100 * count + k) / max;
1557 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1558 * @param v Vehicle that entered a depot.
1560 void VehicleEnterDepot(Vehicle *v)
1562 /* Always work with the front of the vehicle */
1563 assert(v == v->First());
1565 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1567 if (v->type != VEH_TRAIN) {
1568 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1569 * 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 */
1570 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1572 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1574 v->vehstatus |= VS_HIDDEN;
1575 v->cur_speed = 0;
1577 VehicleServiceInDepot(v);
1579 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1580 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1581 v->MarkDirty();
1583 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1584 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1586 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1588 /* Test whether we are heading for this depot. If not, do nothing.
1589 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1590 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1591 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1592 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1593 /* We are heading for another depot, keep driving. */
1594 return;
1597 if (v->current_order.IsRefit()) {
1598 assert (!v->current_order.IsAutoRefit());
1600 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1601 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, CMD_REFIT_VEHICLE);
1602 cur_company.Restore();
1604 if (cost.Failed()) {
1605 _vehicles_to_autoreplace[v] = false;
1606 if (v->owner == _local_company) {
1607 /* Notify the user that we stopped the vehicle */
1608 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_ORDER_REFIT_FAILED, v->index);
1610 } else if (cost.GetCost() != 0) {
1611 v->profit_this_year -= cost.GetCost() << 8;
1612 if (v->owner == _local_company) {
1613 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1618 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1619 /* Part of orders */
1620 v->DeleteUnreachedImplicitOrders();
1621 UpdateVehicleTimetable(v, true);
1622 v->IncrementImplicitOrderIndex();
1624 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1625 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1626 _vehicles_to_autoreplace[v] = false;
1627 /* Invalidate last_loading_station. As the link from the station
1628 * before the stop to the station after the stop can't be predicted
1629 * we shouldn't construct it when the vehicle visits the next stop. */
1630 v->last_loading_station = INVALID_STATION;
1631 if (v->owner == _local_company) {
1632 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1634 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1636 v->current_order.MakeDummy();
1642 * Update the position of the vehicle. This will update the hash that tells
1643 * which vehicles are on a tile.
1645 void Vehicle::UpdatePosition()
1647 vehicle_tile_hash.update (this);
1651 * Update the vehicle on the viewport, updating the right hash and setting the
1652 * new coordinates.
1653 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1655 void Vehicle::UpdateViewport(bool dirty)
1657 Rect new_coord;
1658 this->sprite_seq.GetBounds(&new_coord);
1660 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1661 new_coord.left += pt.x;
1662 new_coord.top += pt.y;
1663 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1664 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1666 vehicle_viewport_hash.update (this, new_coord.left, new_coord.top);
1668 Rect old_coord = this->coord;
1669 this->coord = new_coord;
1671 if (dirty) {
1672 if (old_coord.left == INVALID_COORD) {
1673 this->MarkAllViewportsDirty();
1674 } else {
1675 ::MarkAllViewportsDirty(
1676 min(old_coord.left, this->coord.left),
1677 min(old_coord.top, this->coord.top),
1678 max(old_coord.right, this->coord.right),
1679 max(old_coord.bottom, this->coord.bottom));
1685 * Update the position of the vehicle, and update the viewport.
1687 void Vehicle::UpdatePositionAndViewport()
1689 this->UpdatePosition();
1690 this->UpdateViewport(true);
1694 * Marks viewports dirty where the vehicle's image is.
1696 void Vehicle::MarkAllViewportsDirty() const
1698 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1702 * Get position information of a vehicle when moving one pixel in the direction it is facing
1703 * @param v Vehicle to move
1704 * @return Position information after the move
1706 FullPosTile GetNewVehiclePos(const Vehicle *v)
1708 FullPosTile gp;
1709 gp.set_towards (v->x_pos, v->y_pos, v->direction);
1710 return gp;
1713 static const Direction _new_direction_table[] = {
1714 DIR_N, DIR_NW, DIR_W,
1715 DIR_NE, DIR_SE, DIR_SW,
1716 DIR_E, DIR_SE, DIR_S
1719 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1721 int i = 0;
1723 if (y >= v->y_pos) {
1724 if (y != v->y_pos) i += 3;
1725 i += 3;
1728 if (x >= v->x_pos) {
1729 if (x != v->x_pos) i++;
1730 i++;
1733 Direction dir = v->direction;
1735 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1736 if (dirdiff == DIRDIFF_SAME) return dir;
1737 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1741 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1742 * struct initialization, except after each call to this->NextID() the returned value
1743 * is assigned to a vehicle.
1744 * @param type type of vehicle
1745 * @param owner owner of vehicles
1747 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1749 /* Find maximum */
1750 const Vehicle *v;
1751 FOR_ALL_VEHICLES(v) {
1752 if (v->type == type && v->owner == owner) {
1753 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1757 if (this->maxid == 0) return;
1759 /* Reserving 'maxid + 2' because we need:
1760 * - space for the last item (with v->unitnumber == maxid)
1761 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1762 this->cache = xcalloct<bool>(this->maxid + 2);
1764 /* Fill the cache */
1765 FOR_ALL_VEHICLES(v) {
1766 if (v->type == type && v->owner == owner) {
1767 this->cache[v->unitnumber] = true;
1772 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1773 UnitID FreeUnitIDGenerator::NextID()
1775 if (this->maxid <= this->curid) return ++this->curid;
1777 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1779 return this->curid;
1783 * Get an unused unit number for a vehicle (if allowed).
1784 * @param type Type of vehicle
1785 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1787 UnitID GetFreeUnitNumber(VehicleType type)
1789 /* Check whether it is allowed to build another vehicle. */
1790 uint max_veh;
1791 switch (type) {
1792 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1793 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1794 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1795 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1796 default: NOT_REACHED();
1799 const Company *c = Company::Get(_current_company);
1800 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1802 FreeUnitIDGenerator gen(type, _current_company);
1804 return gen.NextID();
1809 * Check whether we can build infrastructure for the given
1810 * vehicle type. This to disable building stations etc. when
1811 * you are not allowed/able to have the vehicle type yet.
1812 * @param type the vehicle type to check this for
1813 * @return true if there is any reason why you may build
1814 * the infrastructure for the given vehicle type
1816 bool CanBuildVehicleInfrastructure(VehicleType type)
1818 assert(IsCompanyBuildableVehicleType(type));
1820 if (!Company::IsValidID(_local_company)) return false;
1821 if (!_settings_client.gui.disable_unsuitable_building) return true;
1823 UnitID max;
1824 switch (type) {
1825 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1826 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1827 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1828 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1829 default: NOT_REACHED();
1832 /* We can build vehicle infrastructure when we may build the vehicle type */
1833 if (max > 0) {
1834 /* Can we actually build the vehicle type? */
1835 const Engine *e;
1836 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1837 if (HasBit(e->company_avail, _local_company)) return true;
1839 return false;
1842 /* We should be able to build infrastructure when we have the actual vehicle type */
1843 const Vehicle *v;
1844 FOR_ALL_VEHICLES(v) {
1845 if (v->owner == _local_company && v->type == type) return true;
1848 return false;
1853 * Determines the #LiveryScheme for a vehicle.
1854 * @param engine_type Engine of the vehicle.
1855 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1856 * @param v the vehicle, \c NULL if in purchase list etc.
1857 * @return livery scheme to use.
1859 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1861 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1862 const Engine *e = Engine::Get(engine_type);
1863 switch (e->type) {
1864 default: NOT_REACHED();
1865 case VEH_TRAIN:
1866 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1867 /* Wagonoverrides use the colour scheme of the front engine.
1868 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1869 engine_type = parent_engine_type;
1870 e = Engine::Get(engine_type);
1871 /* Note: Luckily cargo_type is not needed for engines */
1874 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1875 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1876 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1877 if (!CargoSpec::Get(cargo_type)->is_freight) {
1878 if (parent_engine_type == INVALID_ENGINE) {
1879 return LS_PASSENGER_WAGON_STEAM;
1880 } else {
1881 switch (RailVehInfo(parent_engine_type)->engclass) {
1882 default: NOT_REACHED();
1883 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1884 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1885 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1886 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1887 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1890 } else {
1891 return LS_FREIGHT_WAGON;
1893 } else {
1894 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1896 switch (e->u.rail.engclass) {
1897 default: NOT_REACHED();
1898 case EC_STEAM: return LS_STEAM;
1899 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1900 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1901 case EC_MONORAIL: return LS_MONORAIL;
1902 case EC_MAGLEV: return LS_MAGLEV;
1906 case VEH_ROAD:
1907 /* Always use the livery of the front */
1908 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1909 engine_type = parent_engine_type;
1910 e = Engine::Get(engine_type);
1911 cargo_type = v->First()->cargo_type;
1913 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1914 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1916 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1917 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1918 /* Tram */
1919 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1920 } else {
1921 /* Bus or truck */
1922 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1925 case VEH_SHIP:
1926 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1927 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1928 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1930 case VEH_AIRCRAFT:
1931 switch (e->u.air.subtype) {
1932 case AIR_HELI: return LS_HELICOPTER;
1933 case AIR_CTOL: return LS_SMALL_PLANE;
1934 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1935 default: NOT_REACHED();
1941 * Determines the livery for a vehicle.
1942 * @param engine_type EngineID of the vehicle
1943 * @param company Owner of the vehicle
1944 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1945 * @param v the vehicle. NULL if in purchase list etc.
1946 * @param livery_setting The livery settings to use for acquiring the livery information.
1947 * @return livery to use
1949 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1951 const Company *c = Company::Get(company);
1952 LiveryScheme scheme = LS_DEFAULT;
1954 /* The default livery is always available for use, but its in_use flag determines
1955 * whether any _other_ liveries are in use. */
1956 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1957 /* Determine the livery scheme to use */
1958 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1960 /* Switch back to the default scheme if the resolved scheme is not in use */
1961 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1964 return &c->livery[scheme];
1968 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1970 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1972 /* Return cached value if any */
1973 if (map != PAL_NONE) return map;
1975 const Engine *e = Engine::Get(engine_type);
1977 /* Check if we should use the colour map callback */
1978 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1979 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1980 /* Failure means "use the default two-colour" */
1981 if (callback != CALLBACK_FAILED) {
1982 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1983 map = GB(callback, 0, 14);
1984 /* If bit 14 is set, then the company colours are applied to the
1985 * map else it's returned as-is. */
1986 if (!HasBit(callback, 14)) {
1987 /* Update cache */
1988 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1989 return map;
1994 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1996 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1998 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1999 if (!Company::IsValidID(company)) return map;
2001 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2003 map += livery->colour1;
2004 if (twocc) map += livery->colour2 * 16;
2006 /* Update cache */
2007 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
2008 return map;
2012 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2013 * @param engine_type ID of engine
2014 * @param company ID of company
2015 * @return A ready-to-use palette modifier
2017 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2019 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
2023 * Get the colour map for a vehicle.
2024 * @param v Vehicle to get colour map for
2025 * @return A ready-to-use palette modifier
2027 PaletteID GetVehiclePalette(const Vehicle *v)
2029 EngineID parent = v->IsGroundVehicle() ?
2030 GroundVehicleBase::From(v)->gcache.first_engine :
2031 INVALID_ENGINE;
2033 return GetEngineColourMap (v->engine_type, v->owner, parent, v);
2037 * Delete all implicit orders which were not reached.
2039 void Vehicle::DeleteUnreachedImplicitOrders()
2041 if (this->IsGroundVehicle()) {
2042 GroundVehicleBase *gv = GroundVehicleBase::From (this);
2043 if (HasBit(gv->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2044 /* Do not delete orders, only skip them */
2045 ClrBit(gv->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2046 this->cur_implicit_order_index = this->cur_real_order_index;
2047 InvalidateVehicleOrder(this, 0);
2048 return;
2052 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2053 while (order != NULL) {
2054 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2056 if (order->IsType(OT_IMPLICIT)) {
2057 DeleteOrder(this, this->cur_implicit_order_index);
2058 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2059 order = this->GetOrder(this->cur_implicit_order_index);
2060 } else {
2061 /* Skip non-implicit orders, e.g. service-orders */
2062 order = order->next;
2063 this->cur_implicit_order_index++;
2066 /* Wrap around */
2067 if (order == NULL) {
2068 order = this->GetOrder(0);
2069 this->cur_implicit_order_index = 0;
2075 * Prepare everything to begin the loading when arriving at a station.
2076 * @pre IsStationTile(this->tile) || this->type == VEH_SHIP.
2078 void Vehicle::BeginLoading()
2080 assert(IsStationTile(this->tile) || this->type == VEH_SHIP);
2082 if (this->current_order.IsType(OT_GOTO_STATION) &&
2083 this->current_order.GetDestination() == this->last_station_visited) {
2084 this->DeleteUnreachedImplicitOrders();
2086 /* Now both order indices point to the destination station, and we can start loading */
2087 this->current_order.MakeLoading(true);
2088 UpdateVehicleTimetable(this, true);
2090 /* Furthermore add the Non Stop flag to mark that this station
2091 * is the actual destination of the vehicle, which is (for example)
2092 * necessary to be known for HandleTrainLoading to determine
2093 * whether the train is lost or not; not marking a train lost
2094 * that arrives at random stations is bad. */
2095 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2097 } else {
2098 /* We weren't scheduled to stop here. Insert an implicit order
2099 * to show that we are stopping here.
2100 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2101 * the 'wrong' terminal when skipping orders etc. */
2102 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2103 if (this->IsGroundVehicle() &&
2104 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2105 in_list->GetDestination() != this->last_station_visited)) {
2106 bool suppress_implicit_orders = HasBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2107 /* Do not create consecutive duplicates of implicit orders */
2108 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2109 if (prev_order == NULL ||
2110 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2111 prev_order->GetDestination() != this->last_station_visited) {
2113 /* Prefer deleting implicit orders instead of inserting new ones,
2114 * so test whether the right order follows later. In case of only
2115 * implicit orders treat the last order in the list like an
2116 * explicit one, except if the overall number of orders surpasses
2117 * IMPLICIT_ORDER_ONLY_CAP. */
2118 int target_index = this->cur_implicit_order_index;
2119 bool found = false;
2120 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2121 const Order *order = this->GetOrder(target_index);
2122 if (order == NULL) break; // No orders.
2123 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2124 found = true;
2125 break;
2127 target_index++;
2128 if (target_index >= this->orders.list->GetNumOrders()) {
2129 if (this->GetNumManualOrders() == 0 &&
2130 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2131 break;
2133 target_index = 0;
2135 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2138 if (found) {
2139 if (suppress_implicit_orders) {
2140 /* Skip to the found order */
2141 this->cur_implicit_order_index = target_index;
2142 InvalidateVehicleOrder(this, 0);
2143 } else {
2144 /* Delete all implicit orders up to the station we just reached */
2145 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2146 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2147 if (order->IsType(OT_IMPLICIT)) {
2148 DeleteOrder(this, this->cur_implicit_order_index);
2149 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2150 order = this->GetOrder(this->cur_implicit_order_index);
2151 } else {
2152 /* Skip non-implicit orders, e.g. service-orders */
2153 order = order->next;
2154 this->cur_implicit_order_index++;
2157 /* Wrap around */
2158 if (order == NULL) {
2159 order = this->GetOrder(0);
2160 this->cur_implicit_order_index = 0;
2162 assert(order != NULL);
2165 } else if (!suppress_implicit_orders &&
2166 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2167 Order::CanAllocateItem()) {
2168 /* Insert new implicit order */
2169 Order *implicit_order = new Order();
2170 implicit_order->MakeImplicit(this->last_station_visited);
2171 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2172 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2174 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2175 * Reenable it for this vehicle */
2176 ClrBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2180 this->current_order.MakeLoading(false);
2183 if (this->last_loading_station != INVALID_STATION &&
2184 this->last_loading_station != this->last_station_visited &&
2185 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2186 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2187 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2190 PrepareUnload(this);
2192 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2193 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2194 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2195 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2197 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2198 this->cur_speed = 0;
2199 this->MarkDirty();
2203 * Return all reserved cargo packets to the station and reset all packets
2204 * staged for transfer.
2205 * @param st the station where the reserved packets should go.
2207 void Vehicle::CancelReservation (Station *st)
2209 for (Vehicle *v = this; v != NULL; v = v->next) {
2210 VehicleCargoList &cargo = v->cargo;
2211 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2212 DEBUG(misc, 1, "cancelling cargo reservation");
2213 cargo.Return (&st->goods[v->cargo_type].cargo);
2214 cargo.SetTransferLoadPlace(st->xy);
2216 cargo.KeepAll();
2221 * Perform all actions when leaving a station.
2222 * @pre this->current_order.IsType(OT_LOADING)
2224 void Vehicle::LeaveStation()
2226 assert(this->current_order.IsType(OT_LOADING));
2228 delete this->cargo_payment;
2229 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2231 /* Only update the timetable if the vehicle was supposed to stop here. */
2232 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2234 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2235 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2236 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2237 /* Refresh next hop stats to make sure we've done that at least once
2238 * during the stop and that refit_cap == cargo_cap for each vehicle in
2239 * the consist. */
2240 this->ResetRefitCaps();
2241 LinkRefresher::Run(this);
2243 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2244 this->last_loading_station = this->last_station_visited;
2245 } else {
2246 /* if the vehicle couldn't load and had to unload or transfer everything
2247 * set the last loading station to invalid as it will leave empty. */
2248 this->last_loading_station = INVALID_STATION;
2252 this->current_order.MakeLeaveStation();
2253 Station *st = Station::Get(this->last_station_visited);
2254 this->CancelReservation (st);
2255 st->loading_vehicles.remove(this);
2257 HideFillingPercent(&this->fill_percent_te_id);
2258 trip_occupancy = CalcPercentVehicleFilled(this, NULL);
2260 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2261 /* Trigger station animation (trains only) */
2262 if (IsStationTile(this->tile)) {
2263 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2264 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2267 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2270 this->MarkDirty();
2274 * Reset all refit_cap in the consist to cargo_cap.
2276 void Vehicle::ResetRefitCaps()
2278 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2282 * Handle the loading of the vehicle; when not it skips through dummy
2283 * orders and does nothing in all other cases.
2284 * @param mode is the non-first call for this vehicle in this tick?
2286 void Vehicle::HandleLoading(bool mode)
2288 switch (this->current_order.GetType()) {
2289 case OT_LOADING: {
2290 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2292 /* Not the first call for this tick, or still loading */
2293 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2295 this->PlayLeaveStationSound();
2297 this->LeaveStation();
2299 /* Only advance to next order if we just loaded at the current one */
2300 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2301 if (order == NULL ||
2302 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2303 order->GetDestination() != this->last_station_visited) {
2304 return;
2306 break;
2309 case OT_DUMMY: break;
2311 default: return;
2314 this->IncrementImplicitOrderIndex();
2318 * Send this vehicle to the depot using the given command(s).
2319 * @param flags the command flags (like execute and such).
2320 * @param command the command to execute.
2321 * @return the cost of the depot action.
2323 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2325 CommandCost ret = CheckOwnership(this->owner);
2326 if (ret.Failed()) return ret;
2328 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2329 if (this->IsStoppedInDepot()) return CMD_ERROR;
2331 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2332 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2333 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2334 /* We called with a different DEPOT_SERVICE setting.
2335 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2336 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2337 if (flags & DC_EXEC) {
2338 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2339 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2340 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2342 return CommandCost();
2345 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2346 if (flags & DC_EXEC) {
2347 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2348 * then skip to the next order; effectively cancelling this forced service */
2349 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2351 if (this->IsGroundVehicle()) {
2352 SetBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2355 this->current_order.MakeDummy();
2356 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2358 return CommandCost();
2361 TileIndex location;
2362 DestinationID destination;
2363 bool reverse;
2364 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};
2365 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2367 if (flags & DC_EXEC) {
2368 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2370 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2371 SetBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2374 this->dest_tile = location;
2375 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2376 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2377 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2379 /* If there is no depot in front, reverse automatically (trains only) */
2380 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2382 if (this->type == VEH_AIRCRAFT) {
2383 Aircraft *a = Aircraft::From(this);
2384 if (a->state == FLYING && a->targetairport != destination) {
2385 /* The aircraft is now heading for a different hangar than the next in the orders */
2386 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2387 AircraftNextAirportPos_and_Order(a);
2392 return CommandCost();
2397 * Update the cached visual effect.
2398 * @param allow_power_change true if the wagon-is-powered-state may change.
2400 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2402 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2403 const Engine *e = this->GetEngine();
2405 /* Evaluate properties */
2406 byte visual_effect;
2407 switch (e->type) {
2408 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2409 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2410 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2411 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2414 /* Check powered wagon / visual effect callback */
2415 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2416 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2418 if (callback != CALLBACK_FAILED) {
2419 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2421 callback = GB(callback, 0, 8);
2422 /* Avoid accidentally setting 'visual_effect' to the default value
2423 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2424 if (callback == VE_DEFAULT) {
2425 assert(HasBit(callback, VE_DISABLE_EFFECT));
2426 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2428 visual_effect = callback;
2432 /* Apply default values */
2433 if (visual_effect == VE_DEFAULT ||
2434 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2435 /* Only train engines have default effects.
2436 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2437 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2438 if (visual_effect == VE_DEFAULT) {
2439 visual_effect = 1 << VE_DISABLE_EFFECT;
2440 } else {
2441 SetBit(visual_effect, VE_DISABLE_EFFECT);
2443 } else {
2444 if (visual_effect == VE_DEFAULT) {
2445 /* Also set the offset */
2446 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2448 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2452 this->vcache.cached_vis_effect = visual_effect;
2454 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2455 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2456 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2460 static const int8 _vehicle_smoke_pos[8] = {
2461 1, 1, 1, 0, -1, -1, -1, 0
2465 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2466 * @param v Vehicle to create effects for.
2468 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2470 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2471 if (callback == CALLBACK_FAILED) return;
2473 uint count = GB(callback, 0, 2);
2474 bool auto_center = HasBit(callback, 13);
2475 bool auto_rotate = !HasBit(callback, 14);
2477 int8 l_center = 0;
2478 if (auto_center) {
2479 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2480 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2481 } else {
2482 /* For trains: Compute offset from vehicle position to sprite position */
2483 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2486 Direction l_dir = v->direction;
2487 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2488 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2490 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2491 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2493 for (uint i = 0; i < count; i++) {
2494 uint32 reg = GetRegister(0x100 + i);
2495 uint type = GB(reg, 0, 8);
2496 int8 x = GB(reg, 8, 8);
2497 int8 y = GB(reg, 16, 8);
2498 int8 z = GB(reg, 24, 8);
2500 if (auto_rotate) {
2501 int8 l = x;
2502 int8 t = y;
2503 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2504 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2507 if (type >= 0xF0) {
2508 switch (type) {
2509 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2510 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2511 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2512 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2513 default: break;
2520 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2521 * @pre this->IsPrimaryVehicle()
2523 void Vehicle::ShowVisualEffect() const
2525 assert(this->IsPrimaryVehicle());
2526 bool sound = false;
2528 /* Do not show any smoke when:
2529 * - vehicle smoke is disabled by the player
2530 * - the vehicle is slowing down or stopped (by the player)
2531 * - the vehicle is moving very slowly
2533 if (_settings_game.vehicle.smoke_amount == 0 ||
2534 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2535 this->cur_speed < 2) {
2536 return;
2539 /* Use the speed as limited by underground and orders. */
2540 uint max_speed = this->GetCurrentMaxSpeed();
2541 if (max_speed == 0) return;
2543 if (this->type == VEH_TRAIN) {
2544 const Train *t = Train::From(this);
2545 /* For trains, do not show any smoke when:
2546 * - the train is reversing
2547 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2549 if (HasBit(t->flags, VRF_REVERSING) ||
2550 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2551 t->cur_speed >= max_speed)) {
2552 return;
2556 const Vehicle *v = this;
2558 do {
2559 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2560 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2561 VisualEffectSpawnModel effect_model = VESM_NONE;
2562 if (advanced) {
2563 effect_offset = VE_OFFSET_CENTRE;
2564 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2565 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2566 } else {
2567 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2568 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2569 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2570 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2571 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2574 /* Show no smoke when:
2575 * - Smoke has been disabled for this vehicle
2576 * - The vehicle is not visible
2577 * - The vehicle is under a bridge
2578 * - The vehicle is on a depot tile
2579 * - The vehicle is on a tunnel tile
2580 * - The vehicle is a train engine that is currently unpowered */
2581 if (effect_model == VESM_NONE ||
2582 v->vehstatus & VS_HIDDEN ||
2583 HasBridgeAbove(v->tile) ||
2584 IsDepotTile(v->tile) ||
2585 IsTunnelTile(v->tile) ||
2586 (v->type == VEH_TRAIN &&
2587 !HasPowerOnRail(Train::From(v)->railtype, Train::From(v)->GetTrackRailType()))) {
2588 continue;
2591 EffectVehicleType evt = EV_END;
2592 switch (effect_model) {
2593 case VESM_STEAM:
2594 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2595 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2596 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2597 * REGULATION:
2598 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2599 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2600 evt = EV_STEAM_SMOKE;
2602 break;
2604 case VESM_DIESEL: {
2605 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2606 * when smoke emission stops.
2607 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2608 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2609 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2610 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2611 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2612 * maximum speed no diesel_smoke is emitted.
2613 * REGULATION:
2614 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2615 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2616 int power_weight_effect = 0;
2617 if (v->type == VEH_TRAIN) {
2618 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2620 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2621 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2622 evt = EV_DIESEL_SMOKE;
2624 break;
2627 case VESM_ELECTRIC:
2628 /* Electric train's spark - more often occurs when train is departing (more load)
2629 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2630 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2631 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2632 * REGULATION:
2633 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2634 if (GB(v->tick_counter, 0, 2) == 0 &&
2635 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2636 evt = EV_ELECTRIC_SPARK;
2638 break;
2640 default:
2641 NOT_REACHED();
2644 if (evt != EV_END && advanced) {
2645 sound = true;
2646 SpawnAdvancedVisualEffect(v);
2647 } else if (evt != EV_END) {
2648 sound = true;
2650 /* The effect offset is relative to a point 4 units behind the vehicle's
2651 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2652 * correction factor. */
2653 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2655 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2656 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2658 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2659 x = -x;
2660 y = -y;
2663 CreateEffectVehicleRel(v, x, y, 10, evt);
2665 } while ((v = v->Next()) != NULL);
2667 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2671 * Set the next vehicle of this vehicle.
2672 * @param next the next vehicle. NULL removes the next vehicle.
2674 void Vehicle::SetNext(Vehicle *next)
2676 assert(this != next);
2678 if (this->next != NULL) {
2679 /* We had an old next vehicle. Update the first and previous pointers */
2680 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2681 v->first = this->next;
2683 this->next->previous = NULL;
2686 this->next = next;
2688 if (this->next != NULL) {
2689 /* A new next vehicle. Update the first and previous pointers */
2690 if (this->next->previous != NULL) this->next->previous->next = NULL;
2691 this->next->previous = this;
2692 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2693 v->first = this->first;
2699 * Adds this vehicle to a shared vehicle chain.
2700 * @param shared_chain a vehicle of the chain with shared vehicles.
2701 * @pre !this->IsOrderListShared()
2703 void Vehicle::AddToShared(Vehicle *shared_chain)
2705 assert(this->previous_shared == NULL && this->next_shared == NULL);
2707 if (shared_chain->orders.list == NULL) {
2708 assert(shared_chain->previous_shared == NULL);
2709 assert(shared_chain->next_shared == NULL);
2710 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2713 this->next_shared = shared_chain->next_shared;
2714 this->previous_shared = shared_chain;
2716 shared_chain->next_shared = this;
2718 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2720 shared_chain->orders.list->AddVehicle(this);
2724 * Removes the vehicle from the shared order list.
2726 void Vehicle::RemoveFromShared()
2728 /* Remember if we were first and the old window number before RemoveVehicle()
2729 * as this changes first if needed. */
2730 bool were_first = (this->FirstShared() == this);
2731 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2733 this->orders.list->RemoveVehicle(this);
2735 if (!were_first) {
2736 /* We are not the first shared one, so only relink our previous one. */
2737 this->previous_shared->next_shared = this->NextShared();
2740 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2743 if (this->orders.list->GetNumVehicles() == 1) {
2744 /* When there is only one vehicle, remove the shared order list window. */
2745 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2746 InvalidateVehicleOrder(this->FirstShared(), 0);
2747 } else if (were_first) {
2748 /* If we were the first one, update to the new first one.
2749 * Note: FirstShared() is already the new first */
2750 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2753 this->next_shared = NULL;
2754 this->previous_shared = NULL;
2757 void VehiclesYearlyLoop()
2759 Vehicle *v;
2760 FOR_ALL_VEHICLES(v) {
2761 if (v->IsPrimaryVehicle()) {
2762 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2763 Money profit = v->GetDisplayProfitThisYear();
2764 if (v->age >= 730 && profit < 0) {
2765 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2766 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index, profit);
2768 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2771 v->profit_last_year = v->profit_this_year;
2772 v->profit_this_year = 0;
2773 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2776 GroupStatistics::UpdateProfits();
2777 SetWindowClassesDirty(WC_TRAINS_LIST);
2778 SetWindowClassesDirty(WC_SHIPS_LIST);
2779 SetWindowClassesDirty(WC_ROADVEH_LIST);
2780 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2785 * Can this station be used by the given engine type?
2786 * @param engine_type the type of vehicles to test
2787 * @param st the station to test for
2788 * @return true if and only if the vehicle of the type can use this station.
2789 * @note For road vehicles the Vehicle is needed to determine whether it can
2790 * use the station. This function will return true for road vehicles
2791 * when at least one of the facilities is available.
2793 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2795 const Engine *e = Engine::GetIfValid(engine_type);
2796 assert(e != NULL);
2798 switch (e->type) {
2799 case VEH_TRAIN:
2800 return (st->facilities & FACIL_TRAIN) != 0;
2802 case VEH_ROAD:
2803 /* For road vehicles we need the vehicle to know whether it can actually
2804 * use the station, but if it doesn't have facilities for RVs it is
2805 * certainly not possible that the station can be used. */
2806 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2808 case VEH_SHIP:
2809 return (st->facilities & FACIL_DOCK) != 0;
2811 case VEH_AIRCRAFT:
2812 return (st->facilities & FACIL_AIRPORT) != 0 &&
2813 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2815 default:
2816 return false;
2821 * Can this station be used by the given vehicle?
2822 * @param v the vehicle to test
2823 * @param st the station to test for
2824 * @return true if and only if the vehicle can use this station.
2826 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2828 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2830 return CanVehicleUseStation(v->engine_type, st);
2834 * Calculates the set of vehicles that will be affected by a given selection.
2835 * @param set [inout] Set of affected vehicles.
2836 * @param v First vehicle of the selection.
2837 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2838 * @pre \a set must be empty.
2839 * @post \a set will contain the vehicles that will be refitted.
2841 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2843 if (v->type == VEH_TRAIN) {
2844 Train *u = Train::From(v);
2845 /* Only include whole vehicles, so start with the first articulated part */
2846 u = u->GetFirstEnginePart();
2848 /* Include num_vehicles vehicles, not counting articulated parts */
2849 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2850 do {
2851 /* Include current vehicle in the selection. */
2852 set.Include(u->index);
2854 /* If the vehicle is multiheaded, add the other part too. */
2855 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2857 u = u->Next();
2858 } while (u != NULL && u->IsArticulatedPart());