Add VehicleSpriteSeq::GetBounds and Draw
[openttd/fttd.git] / src / vehicle.cpp
blobfbdcdf6b631eb6b19780780b138b327d8312ecf7
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 const Sprite *spr = GetSprite(this->sprite, ST_NORMAL);
77 bounds->left = spr->x_offs;
78 bounds->top = spr->y_offs;
79 bounds->right = spr->width + spr->x_offs - 1;
80 bounds->bottom = spr->height + spr->y_offs - 1;
83 /**
84 * Draw the sprite sequence.
85 * @param dpi The area to draw on.
86 * @param x X position
87 * @param y Y position
88 * @param default_pal Vehicle palette
89 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
91 void VehicleSpriteSeq::Draw (BlitArea *dpi, int x, int y, PaletteID default_pal, bool force_pal) const
93 DrawSprite (dpi, this->sprite, default_pal, x, y);
96 /**
97 * Function to tell if a vehicle needs to be autorenewed
98 * @param *c The vehicle owner
99 * @return true if the vehicle is old enough for replacement
101 bool Vehicle::NeedsAutorenewing (const Company *c) const
103 /* We can always generate the Company pointer when we have the vehicle.
104 * However this takes time and since the Company pointer is often present
105 * when this function is called then it's faster to pass the pointer as an
106 * argument rather than finding it again. */
107 assert(c == Company::Get(this->owner));
109 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
111 /* Only engines need renewing */
112 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
114 return true;
118 * Service a vehicle and all subsequent vehicles in the consist
120 * @param *v The vehicle or vehicle chain being serviced
122 void VehicleServiceInDepot(Vehicle *v)
124 assert(v != NULL);
125 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
127 do {
128 v->date_of_last_service = _date;
129 v->breakdowns_since_last_service = 0;
130 v->reliability = v->GetEngine()->reliability;
131 /* Prevent vehicles from breaking down directly after exiting the depot. */
132 v->breakdown_chance /= 4;
133 v = v->Next();
134 } while (v != NULL && v->HasEngineType());
138 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
140 * @see NeedsAutomaticServicing()
141 * @param scheduled Whether the check is part of a scheduled order.
142 * @return true if the vehicle should go to a depot if a opportunity presents itself.
144 bool Vehicle::NeedsServicing (bool scheduled) const
146 /* Stopped or crashed vehicles will not move, as such making unmovable
147 * vehicles to go for service is lame. */
148 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
150 /* Are we ready for the next service cycle? */
151 const Company *c = Company::Get(this->owner);
152 if (this->ServiceIntervalIsPercent() ?
153 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
154 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
155 return false;
158 /* Service if playing with breakdowns. */
159 if (_settings_game.difficulty.vehicle_breakdowns != 0) return true;
161 /* Check company setting for servicing when breakdowns are disabled. */
162 bool autorenew = c->settings.engine_renew;
163 switch (c->settings.servicing_if_no_breakdowns) {
164 case 0: autorenew &= scheduled; break;
165 case 1: break;
166 default: return true;
169 /* Test whether there is some pending autoreplace.
170 * Note: We do this after the service-interval test.
171 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
172 bool pending_replace = false;
173 Money needed_money = c->settings.engine_renew_money;
174 if (needed_money > c->money) return false;
176 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
177 bool replace_when_old = false;
178 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
180 if (new_engine == INVALID_ENGINE) {
181 if (!autorenew) continue;
182 new_engine = v->engine_type;
183 replace_when_old = true;
186 /* Check engine availability */
187 if (!HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
188 /* Is the vehicle old if we are not always replacing? */
189 if (replace_when_old && !v->NeedsAutorenewing(c)) continue;
191 /* Check refittability */
192 uint32 available_cargo_types, union_mask;
193 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
194 /* Is there anything to refit? */
195 if (union_mask != 0) {
196 CargoID cargo_type;
197 /* We cannot refit to mixed cargoes in an automated way */
198 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
200 /* Did the old vehicle carry anything? */
201 if (cargo_type != CT_INVALID) {
202 /* We can't refit the vehicle to carry the cargo we want */
203 if (!HasBit(available_cargo_types, cargo_type)) continue;
207 /* Check money.
208 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
209 pending_replace = true;
210 needed_money += 2 * Engine::Get(new_engine)->GetCost();
211 if (needed_money > c->money) return false;
214 return pending_replace;
218 * Checks if the current order should be interrupted for a service-in-depot order.
219 * @see NeedsServicing()
220 * @return true if the current order should be interrupted.
222 bool Vehicle::NeedsAutomaticServicing() const
224 if (this->HasDepotOrder()) return false;
225 if (this->current_order.IsType(OT_LOADING)) return false;
226 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
227 return this->NeedsServicing (false);
230 uint Vehicle::Crash(bool flooded)
232 assert((this->vehstatus & VS_CRASHED) == 0);
233 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
235 uint pass = 0;
236 /* Stop the vehicle. */
237 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
238 /* crash all wagons, and count passengers */
239 for (Vehicle *v = this; v != NULL; v = v->Next()) {
240 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
241 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
242 v->vehstatus |= VS_CRASHED;
243 v->MarkAllViewportsDirty();
246 /* Dirty some windows */
247 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
248 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
249 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
250 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
252 delete this->cargo_payment;
253 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
255 return RandomRange(pass + 1); // Randomise deceased passengers.
260 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
261 * @param engine The engine that caused the problem
262 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
263 * @param part2 Part 2 of the error message, taking the engine as parameter 2
264 * @param bug_type Flag to check and set in grfconfig
265 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
267 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
269 const Engine *e = Engine::Get(engine);
270 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
272 /* Missing GRF. Nothing useful can be done in this situation. */
273 if (grfconfig == NULL) return;
275 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
276 SetBit(grfconfig->grf_bugs, bug_type);
277 SetDParamStr(0, grfconfig->GetName());
278 SetDParam(1, engine);
279 ShowErrorMessage(part1, part2, WL_CRITICAL);
280 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
283 /* debug output */
284 char buffer[512];
286 SetDParamStr(0, grfconfig->GetName());
287 GetString (buffer, part1);
288 DEBUG(grf, 0, "%s", buffer + 3);
290 SetDParam(1, engine);
291 GetString (buffer, part2);
292 DEBUG(grf, 0, "%s", buffer + 3);
296 * Logs a bug in GRF and shows a warning message if this
297 * is for the first time this happened.
298 * @param u first vehicle of chain
300 void VehicleLengthChanged(const Vehicle *u)
302 /* show a warning once for each engine in whole game and once for each GRF after each game load */
303 const Engine *engine = u->GetEngine();
304 uint32 grfid = engine->grf_prop.grffile->grfid;
305 GRFConfig *grfconfig = GetGRFConfig(grfid);
306 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
307 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
312 * Vehicle constructor.
313 * @param type Type of the new vehicle.
315 Vehicle::Vehicle(VehicleType type)
317 this->type = type;
318 this->coord.left = INVALID_COORD;
319 this->group_id = DEFAULT_GROUP;
320 this->fill_percent_te_id = INVALID_TE_ID;
321 this->first = this;
322 this->colourmap = PAL_NONE;
323 this->cargo_age_counter = 1;
324 this->last_station_visited = INVALID_STATION;
325 this->last_loading_station = INVALID_STATION;
329 * Get a value for a vehicle's random_bits.
330 * @return A random value from 0 to 255.
332 byte VehicleRandomBits()
334 return GB(Random(), 0, 8);
339 * Vehicle hash update template function
340 * @param v The vehicle to update
341 * @param link The vehicle link to update within Vehicle
342 * @param old_hash The hash to remove the vehicle from, if any
343 * @param new_hash The hash to add the vehicle to, if any
345 static void UpdateVehicleHash(Vehicle *v, VehicleHashLink Vehicle::*link, Vehicle **old_hash, Vehicle **new_hash)
347 if (old_hash == new_hash) return;
349 /* Remove from the old position in the hash table */
350 if (old_hash != NULL) {
351 if ((v->*link).next != NULL) ((v->*link).next->*link).pprev = (v->*link).pprev;
352 *(v->*link).pprev = (v->*link).next;
355 /* Insert vehicle at beginning of the new position in the hash table */
356 if (new_hash != NULL) {
357 (v->*link).next = *new_hash;
358 if ((v->*link).next != NULL) ((v->*link).next->*link).pprev = &(v->*link).next;
359 (v->*link).pprev = new_hash;
360 *new_hash = v;
364 /* Forward declaration for HashAreaIterator */
365 template <uint nx, uint ny>
366 struct HashAreaIterator;
369 * Hash pack template class
370 * @tparam nx Number of bits to use for x.
371 * @tparam ny Number of bits to use for y.
373 template <uint nx, uint ny>
374 struct HashPack {
375 static const uint PACK_BIT0_X = 0;
376 static const uint PACK_BITS_X = nx;
377 static const uint PACK_MASK_X = ((1 << PACK_BITS_X) - 1) << PACK_BIT0_X;
379 static const uint PACK_BIT0_Y = PACK_BIT0_X + PACK_BITS_X;
380 static const uint PACK_BITS_Y = ny;
381 static const uint PACK_MASK_Y = ((1 << PACK_BITS_Y) - 1) << PACK_BIT0_Y;
383 static const uint PACK_BITS = PACK_BIT0_Y + PACK_BITS_Y;
384 static const uint PACK_SIZE = 1 << PACK_BITS;
386 typedef HashAreaIterator <nx, ny> AreaIterator;
388 static inline uint pack_x (uint x)
390 return (x << PACK_BIT0_X) & PACK_MASK_X;
393 static inline uint pack_y (uint y)
395 return (y << PACK_BIT0_Y) & PACK_MASK_Y;
398 static inline uint pack (uint x, uint y)
400 return pack_x(x) + pack_y(y);
403 static inline uint next_x (uint x)
405 return (x + (1 << PACK_BIT0_X)) & PACK_MASK_X;
408 static inline uint next_y (uint y)
410 return (y + (1 << PACK_BIT0_Y)) & PACK_MASK_Y;
415 * Area iterator for a hash class
416 * @tparam nx Number of bits to use for x.
417 * @tparam ny Number of bits to use for y.
419 template <uint nx, uint ny>
420 struct HashAreaIterator : HashPack <nx, ny> {
421 uint x0, x1, y0, y1;
422 uint x, y;
424 void reset (uint xx0, uint xx1, uint yy0, uint yy1)
426 x0 = this->pack_x (xx0);
427 x1 = this->pack_x (xx1);
429 y0 = this->pack_y (yy0);
430 y1 = this->pack_y (yy1);
432 x = x0;
433 y = y0;
436 uint get() const
438 return x + y;
441 bool next()
443 if (x != x1) {
444 x = this->next_x(x);
445 return true;
446 } else if (y != y1) {
447 y = this->next_y(y);
448 x = x0;
449 return true;
450 } else {
451 return false;
457 struct VehicleTileHash : HashPack <7, 7> {
458 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
459 * lookup times at the expense of memory usage. */
461 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
462 * Profiling results show that 0 is fastest. */
463 static const uint HASH_RES = 0;
465 static const uint HASH_OFFSET_X = HASH_RES;
466 static const uint HASH_BITS_X = PACK_BITS_X;
468 static const uint HASH_OFFSET_Y = HASH_RES;
469 static const uint HASH_BITS_Y = PACK_BITS_Y;
471 static const uint HASH_SIZE = PACK_SIZE;
473 Vehicle *buckets[HASH_SIZE];
475 static inline uint hash (int x, int y)
477 return pack (GB(x, HASH_OFFSET_X, HASH_BITS_X), GB(y, HASH_OFFSET_Y, HASH_BITS_Y));
480 static inline uint hash (TileIndex tile)
482 return hash (TileX(tile), TileY(tile));
485 inline Vehicle **get_bucket (TileIndex tile)
487 return &this->buckets[hash(tile)];
490 void update (Vehicle *v, bool remove = false)
492 Vehicle **new_hash = remove ? NULL : get_bucket(v->tile);
494 UpdateVehicleHash(v, &Vehicle::hash_tile_link,
495 v->hash_tile_current, new_hash);
497 /* Remember current hash position */
498 v->hash_tile_current = new_hash;
501 void reset()
503 Vehicle *v;
504 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
505 memset (this->buckets, 0, sizeof(this->buckets));
508 void setup_iter (AreaIterator *iter, int xl, int xu, int yl, int yu)
510 iter->reset (GB(xl, HASH_OFFSET_X, HASH_BITS_X), GB(xu, HASH_OFFSET_X, HASH_BITS_X),
511 GB(yl, HASH_OFFSET_Y, HASH_BITS_Y), GB(yu, HASH_OFFSET_Y, HASH_BITS_Y));
515 static VehicleTileHash vehicle_tile_hash;
518 * Get the first vehicle for a VehicleTileIterator
519 * @param tile The tile to iterate at
521 Vehicle *VehicleTileIterator::first (TileIndex tile)
523 return *vehicle_tile_hash.get_bucket(tile);
528 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
529 * @note Do not call this function directly!
530 * @param x The X location on the map
531 * @param y The Y location on the map
532 * @param data Arbitrary data passed to proc
533 * @param proc The proc that determines whether a vehicle will be "found".
534 * @param find_first Whether to return on the first found or iterate over
535 * all vehicles
536 * @return the best matching or first vehicle (depending on find_first).
538 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
540 const int COLL_DIST = 6;
542 /* Hash area to scan is from xl,yl to xu,yu */
543 VehicleTileHash::AreaIterator iter;
544 vehicle_tile_hash.setup_iter (&iter,
545 (x - COLL_DIST) / TILE_SIZE, (x + COLL_DIST) / TILE_SIZE,
546 (y - COLL_DIST) / TILE_SIZE, (y + COLL_DIST) / TILE_SIZE);
548 do {
549 Vehicle *v = vehicle_tile_hash.buckets[iter.get()];
550 for (; v != NULL; v = v->hash_tile_link.next) {
551 Vehicle *a = proc(v, data);
552 if (find_first && a != NULL) return a;
554 } while (iter.next());
556 return NULL;
560 * Find a vehicle from a specific location. It will call proc for ALL vehicles
561 * on the tile and YOU must make SURE that the "best one" is stored in the
562 * data value and is ALWAYS the same regardless of the order of the vehicles
563 * where proc was called on!
564 * When you fail to do this properly you create an almost untraceable DESYNC!
565 * @note The return value of proc will be ignored.
566 * @note Use this when you have the intention that all vehicles
567 * should be iterated over.
568 * @param x The X location on the map
569 * @param y The Y location on the map
570 * @param data Arbitrary data passed to proc
571 * @param proc The proc that determines whether a vehicle will be "found".
573 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
575 VehicleFromPosXY(x, y, data, proc, false);
579 * Checks whether a vehicle in on a specific location. It will call proc for
580 * vehicles until it returns non-NULL.
581 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
582 * should be iterated over.
583 * @param x The X location on the map
584 * @param y The Y location on the map
585 * @param data Arbitrary data passed to proc
586 * @param proc The proc that determines whether a vehicle will be "found".
587 * @return True if proc returned non-NULL.
589 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
591 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
595 * Ensure there is no vehicle at the ground at the given position.
596 * @param tile Position to examine.
597 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
599 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
601 int z = GetTileMaxPixelZ(tile);
603 VehicleTileFinder iter (tile);
604 Vehicle *v = NULL;
605 while (!iter.finished()) {
606 v = iter.next();
608 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) continue;
609 if (v->z_pos > z) continue;
611 iter.set_found();
614 /* Value v is not safe in MP games, however, it is used to generate a local
615 * error message only (which may be different for different machines).
616 * Such a message does not affect MP synchronisation.
618 if (iter.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
619 return CommandCost();
623 * Finds vehicle in tunnel / bridge
624 * @param tile first end
625 * @param endtile second end
626 * @param ignore Ignore this vehicle when searching
627 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
629 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
631 /* Value v is not safe in MP games, however, it is used to generate a local
632 * error message only (which may be different for different machines).
633 * Such a message does not affect MP synchronisation.
635 Vehicle *v = NULL;
637 VehicleTileFinder iter1 (tile);
638 while (!iter1.finished()) {
639 v = iter1.next();
641 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) continue;
642 if (v == ignore) continue;
644 iter1.set_found();
646 if (iter1.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
648 VehicleTileFinder iter2 (endtile);
649 while (!iter2.finished()) {
650 v = iter2.next();
652 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) continue;
653 if (v == ignore) continue;
655 iter2.set_found();
657 if (iter2.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
659 return CommandCost();
662 static Track AllowedNonOverlappingTrack(TrackBits bits)
664 switch (bits) {
665 case TRACK_BIT_UPPER: return TRACK_LOWER;
666 case TRACK_BIT_LOWER: return TRACK_UPPER;
667 case TRACK_BIT_LEFT: return TRACK_RIGHT;
668 case TRACK_BIT_RIGHT: return TRACK_LEFT;
669 default: return INVALID_TRACK;
674 * Tests if a vehicle interacts with the specified track bits.
675 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
677 * @param tile The tile.
678 * @param track_bits The track bits.
679 * @return \c true if no train that interacts, is found. \c false if a train is found.
681 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
683 assert(track_bits != TRACK_BIT_NONE);
685 Track allowed = AllowedNonOverlappingTrack(track_bits);
687 VehicleTileFinder iter (tile);
688 while (!iter.finished()) {
689 Vehicle *v = iter.next();
690 if (v->type != VEH_TRAIN) continue;
692 Trackdir trackdir = Train::From(v)->trackdir;
693 if (trackdir >= TRACKDIR_END) continue; // in wormhole or depot
695 if (TrackdirToTrack(trackdir) != allowed) iter.set_found();
698 if (iter.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
699 return CommandCost();
702 static bool EnsureNoTrainOnBridgeEndTrackBits (TileIndex tile, TrackBits bits)
704 assert(bits != TRACK_BIT_NONE);
706 Track allowed = AllowedNonOverlappingTrack(bits);
707 VehicleTileFinder iter (tile);
708 while (!iter.finished()) {
709 Vehicle *v = iter.next();
710 if (v->type != VEH_TRAIN) continue;
712 Trackdir trackdir = Train::From(v)->trackdir;
713 if (TrackdirToTrack(trackdir) != allowed) iter.set_found();
716 return !iter.was_found();
720 * Tests if a train interacts with the specified track bits or is on the bridge middle part.
722 * @param tile1 one bridge end
723 * @param bits1 track bits on first bridge end
724 * @param tile2 the other bridge end
725 * @param bits2 track bits on second bridge end
726 * @return whether there is a train on the bridge tracks or middle part
728 CommandCost EnsureNoTrainOnBridgeTrackBits(TileIndex tile1, TrackBits bits1, TileIndex tile2, TrackBits bits2)
730 if (!EnsureNoTrainOnBridgeEndTrackBits (tile1, bits1) ||
731 !EnsureNoTrainOnBridgeEndTrackBits (tile2, bits2)) {
732 return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
735 return CommandCost();
738 static bool EnsureNoTrainOnTunnelBridgeEndMiddle (TileIndex tile)
740 VehicleTileFinder iter (tile);
741 while (!iter.finished()) {
742 Vehicle *v = iter.next();
743 if (v->type == VEH_TRAIN && Train::From(v)->trackdir == TRACKDIR_WORMHOLE) iter.set_found();
745 return !iter.was_found();
749 * Tests if there is a train on the middle bridge part
750 * @param tile1 one bridge end
751 * @param tile2 the other bridge end
752 * @return whether there is a train on the bridge
754 CommandCost EnsureNoTrainOnTunnelBridgeMiddle(TileIndex tile1, TileIndex tile2)
756 if (!EnsureNoTrainOnTunnelBridgeEndMiddle(tile1) ||
757 !EnsureNoTrainOnTunnelBridgeEndMiddle(tile2)) {
758 return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
761 return CommandCost();
765 struct VehicleViewportHash : HashPack <6, 6> {
766 static const uint HASH_OFFSET_X = 7 + ZOOM_LVL_SHIFT;
767 static const uint HASH_BITS_X = PACK_BITS_X;
769 static const uint HASH_OFFSET_Y = 6 + ZOOM_LVL_SHIFT;
770 static const uint HASH_BITS_Y = PACK_BITS_Y;
772 static const uint HASH_SIZE = PACK_SIZE;
774 Vehicle *buckets[HASH_SIZE];
776 static inline uint hash (int x, int y)
778 return pack (GB(x, HASH_OFFSET_X, HASH_BITS_X), GB(y, HASH_OFFSET_Y, HASH_BITS_Y));
781 inline Vehicle **get_bucket (int x, int y)
783 return (x == INVALID_COORD) ? NULL : &buckets[hash(x, y)];
786 void update (Vehicle *v, int x, int y)
788 UpdateVehicleHash(v, &Vehicle::hash_viewport_link,
789 this->get_bucket (v->coord.left, v->coord.top),
790 this->get_bucket (x, y));
793 void reset()
795 memset (this->buckets, 0, sizeof(this->buckets));
798 void setup_iter (AreaIterator *iter, int l, uint w, int t, uint h)
800 uint x0, x1, y0, y1;
802 if (w < (1 << (HASH_OFFSET_X + HASH_BITS_X))) {
803 x0 = GB(l, HASH_OFFSET_X, HASH_BITS_X);
804 x1 = GB(l + w, HASH_OFFSET_X, HASH_BITS_X);
805 } else {
806 /* scan whole hash row */
807 x0 = 0;
808 x1 = (1 << HASH_BITS_X) - 1;
811 if (h < (1 << (HASH_OFFSET_Y + HASH_BITS_Y))) {
812 y0 = GB(t, HASH_OFFSET_Y, HASH_BITS_Y);
813 y1 = GB(t + h, HASH_OFFSET_Y, HASH_BITS_Y);
814 } else {
815 /* scan whole column */
816 y0 = 0;
817 y1 = (1 << HASH_BITS_Y) - 1;
820 iter->reset (x0, x1, y0, y1);
825 static VehicleViewportHash vehicle_viewport_hash;
827 void ResetVehicleHash()
829 vehicle_viewport_hash.reset();
830 vehicle_tile_hash.reset();
833 void ResetVehicleColourMap()
835 Vehicle *v;
836 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
840 * List of vehicles that should check for autoreplace this tick.
841 * Mapping of vehicle -> leave depot immediately after autoreplace.
843 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
844 static AutoreplaceMap _vehicles_to_autoreplace;
846 void InitializeVehicles()
848 _vehicles_to_autoreplace.Reset();
849 ResetVehicleHash();
852 uint CountVehiclesInChain(const Vehicle *v)
854 uint count = 0;
855 do count++; while ((v = v->Next()) != NULL);
856 return count;
860 * Check if a vehicle is counted in num_engines in each company struct
861 * @return true if the vehicle is counted in num_engines
863 bool Vehicle::IsEngineCountable() const
865 switch (this->type) {
866 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
867 case VEH_TRAIN:
868 return !this->IsArticulatedPart() && // tenders and other articulated parts
869 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
870 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
871 case VEH_SHIP: return true;
872 default: return false; // Only count company buildable vehicles
877 * Check whether Vehicle::engine_type has any meaning.
878 * @return true if the vehicle has a useable engine type.
880 bool Vehicle::HasEngineType() const
882 switch (this->type) {
883 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
884 case VEH_TRAIN:
885 case VEH_ROAD:
886 case VEH_SHIP: return true;
887 default: return false;
892 * Retrieves the engine of the vehicle.
893 * @return Engine of the vehicle.
894 * @pre HasEngineType() == true
896 const Engine *Vehicle::GetEngine() const
898 return Engine::Get(this->engine_type);
902 * Retrieve the NewGRF the vehicle is tied to.
903 * This is the GRF providing the Action 3 for the engine type.
904 * @return NewGRF associated to the vehicle.
906 const GRFFile *Vehicle::GetGRF() const
908 return this->GetEngine()->GetGRF();
912 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
913 * This is the GRF providing the Action 3 for the engine type.
914 * @return GRF ID of the associated NewGRF.
916 uint32 Vehicle::GetGRFID() const
918 return this->GetEngine()->GetGRFID();
922 * Handle the pathfinding result, especially the lost status.
923 * If the vehicle is now lost and wasn't previously fire an
924 * event to the AIs and a news message to the user. If the
925 * vehicle is not lost anymore remove the news message.
926 * @param path_found Whether the vehicle has a path to its destination.
928 void Vehicle::HandlePathfindingResult(bool path_found)
930 if (path_found) {
931 /* Route found, is the vehicle marked with "lost" flag? */
932 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
934 /* Clear the flag as the PF's problem was solved. */
935 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
936 /* Delete the news item. */
937 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
938 return;
941 /* Were we already lost? */
942 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
944 /* It is first time the problem occurred, set the "lost" flag. */
945 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
946 /* Notify user about the event. */
947 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
948 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
949 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_VEHICLE_IS_LOST, this->index);
953 /** Destroy all stuff that (still) needs the virtual functions to work properly */
954 void Vehicle::PreDestructor()
956 if (CleaningPool()) return;
958 if (Station::IsValidID(this->last_station_visited)) {
959 Station *st = Station::Get(this->last_station_visited);
960 st->loading_vehicles.remove(this);
962 HideFillingPercent(&this->fill_percent_te_id);
963 this->CancelReservation(INVALID_STATION, st);
964 delete this->cargo_payment;
965 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
968 if (this->IsEngineCountable()) {
969 GroupStatistics::CountEngine(this, -1);
970 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
971 GroupStatistics::UpdateAutoreplace(this->owner);
973 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
974 DeleteGroupHighlightOfVehicle(this);
977 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
978 Aircraft *a = Aircraft::From(this);
979 Station *st = GetTargetAirportIfValid(a);
980 if (st != NULL) {
981 const AirportFTA *layout = st->airport.GetFTA()->layout;
982 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
987 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
988 RoadVehicle *v = RoadVehicle::From(this);
989 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
990 /* Leave the drive through roadstop, when you have not already left it. */
991 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->LeaveDriveThrough (TrackdirToExitdir((Trackdir)(v->state & RVSB_ROAD_STOP_TRACKDIR_MASK)), v->gcache.cached_total_length);
995 if (this->Previous() == NULL) {
996 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
999 if (this->IsPrimaryVehicle()) {
1000 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
1001 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
1002 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
1003 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
1004 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
1005 SetWindowDirty(WC_COMPANY, this->owner);
1006 OrderBackup::ClearVehicle(this);
1008 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
1010 this->cargo.Truncate();
1011 DeleteVehicleOrders(this);
1012 DeleteDepotHighlightOfVehicle(this);
1014 extern void StopGlobalFollowVehicle(const Vehicle *v);
1015 StopGlobalFollowVehicle(this);
1017 ReleaseDisastersTargetingVehicle(this->index);
1020 Vehicle::~Vehicle()
1022 if (CleaningPool()) {
1023 this->cargo.OnCleanPool();
1024 return;
1027 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
1028 * it may happen that vehicle chain is deleted when visible */
1029 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
1031 Vehicle *v = this->Next();
1032 this->SetNext(NULL);
1034 delete v;
1036 vehicle_tile_hash.update (this, true);
1037 vehicle_viewport_hash.update (this, INVALID_COORD, 0);
1038 DeleteVehicleNews(this->index, INVALID_STRING_ID);
1039 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
1043 * Adds a vehicle to the list of vehicles that visited a depot this tick
1044 * @param *v vehicle to add
1046 void VehicleEnteredDepotThisTick(Vehicle *v)
1048 /* Vehicle should stop in the depot if it was in 'stopping' state */
1049 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
1051 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
1052 * stopping in the depot, so we stop it to ensure that it will not reserve
1053 * the path out of the depot before we might autoreplace it to a different
1054 * engine. The new engine would not own the reserved path we store that we
1055 * stopped the vehicle, so autoreplace can start it again */
1056 v->vehstatus |= VS_STOPPED;
1060 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
1061 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
1062 * so each day, all vehicles are processes in DAY_TICKS steps.
1064 static void RunVehicleDayProc()
1066 if (_game_mode != GM_NORMAL) return;
1068 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1069 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
1070 Vehicle *v = Vehicle::Get(i);
1071 if (v == NULL) continue;
1073 /* Call the 32-day callback if needed */
1074 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
1075 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
1076 if (callback != CALLBACK_FAILED) {
1077 if (HasBit(callback, 0)) {
1078 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
1081 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1082 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1083 if (callback != 0) v->First()->MarkDirty();
1085 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
1089 /* This is called once per day for each vehicle, but not in the first tick of the day */
1090 v->OnNewDay();
1094 void CallVehicleTicks()
1096 _vehicles_to_autoreplace.Clear();
1098 RunVehicleDayProc();
1100 Station *st;
1101 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
1103 Vehicle *v;
1104 FOR_ALL_VEHICLES(v) {
1105 /* Vehicle could be deleted in this tick */
1106 if (!v->Tick()) {
1107 assert(Vehicle::Get(vehicle_index) == NULL);
1108 continue;
1111 assert(Vehicle::Get(vehicle_index) == v);
1113 switch (v->type) {
1114 default: break;
1116 case VEH_TRAIN:
1117 case VEH_ROAD:
1118 case VEH_AIRCRAFT:
1119 case VEH_SHIP: {
1120 Vehicle *front = v->First();
1122 if (v->vcache.cached_cargo_age_period != 0) {
1123 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
1124 if (--v->cargo_age_counter == 0) {
1125 v->cargo.AgeCargo();
1126 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
1130 /* Do not play any sound when crashed */
1131 if (front->vehstatus & VS_CRASHED) continue;
1133 /* Do not play any sound when in depot or tunnel */
1134 if (v->vehstatus & VS_HIDDEN) continue;
1136 /* Do not play any sound when stopped */
1137 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1139 /* Check vehicle type specifics */
1140 switch (v->type) {
1141 case VEH_TRAIN:
1142 if (Train::From(v)->IsWagon()) continue;
1143 break;
1145 case VEH_ROAD:
1146 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1147 break;
1149 case VEH_AIRCRAFT:
1150 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1151 break;
1153 default:
1154 break;
1157 v->motion_counter += front->cur_speed;
1158 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1159 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1161 /* Play an alternating running sound every 16 ticks */
1162 if (GB(v->tick_counter, 0, 4) == 0) {
1163 /* Play running sound when speed > 0 and not braking */
1164 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1165 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1168 break;
1173 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1174 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1175 v = it->first;
1176 /* Autoreplace needs the current company set as the vehicle owner */
1177 cur_company.Change(v->owner);
1179 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1180 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1181 * they are already leaving the depot again before being replaced. */
1182 if (it->second) v->vehstatus &= ~VS_STOPPED;
1184 /* Store the position of the effect as the vehicle pointer will become invalid later */
1185 int x = v->x_pos;
1186 int y = v->y_pos;
1187 int z = v->z_pos;
1189 const Company *c = Company::Get(_current_company);
1190 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1191 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1192 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1194 if (!IsLocalCompany()) continue;
1196 if (res.Succeeded()) {
1197 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1198 continue;
1201 StringID error_message = res.GetErrorMessage();
1202 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1204 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1206 StringID message;
1207 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1208 message = error_message;
1209 } else {
1210 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1213 AddNewsItem<VehicleAdviceNewsItem> (message, v->index, error_message);
1216 cur_company.Restore();
1220 * Add vehicle sprite for drawing to the screen.
1221 * @param vd Viewport drawer to use.
1222 * @param v Vehicle to draw.
1224 static void DoDrawVehicle (ViewportDrawer *vd, const Vehicle *v)
1226 PaletteID pal = PAL_NONE;
1228 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1230 /* Check whether the vehicle shall be transparent due to the game state */
1231 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1233 if (v->type == VEH_EFFECT) {
1234 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1235 * However, transparent smoke and bubbles look weird, so always hide them. */
1236 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1237 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1240 AddSortableSpriteToDraw (vd, v->sprite_seq.sprite, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1241 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1245 * Add the vehicle sprites that should be drawn at a part of the screen.
1246 * @param dpi Rectangle being drawn.
1248 void ViewportAddVehicles (ViewportDrawer *vd, const DrawPixelInfo *dpi)
1250 /* The bounding rectangle */
1251 const int l = dpi->left;
1252 const int r = dpi->left + dpi->width;
1253 const int t = dpi->top;
1254 const int b = dpi->top + dpi->height;
1256 /* The hash area to scan */
1257 VehicleViewportHash::AreaIterator iter;
1258 vehicle_viewport_hash.setup_iter (&iter,
1259 l - (70 * ZOOM_LVL_BASE), dpi->width + (70 * ZOOM_LVL_BASE),
1260 t - (70 * ZOOM_LVL_BASE), dpi->height + (70 * ZOOM_LVL_BASE));
1262 do {
1263 const Vehicle *v = vehicle_viewport_hash.buckets[iter.get()];
1265 while (v != NULL) {
1266 if (!(v->vehstatus & VS_HIDDEN) &&
1267 l <= v->coord.right &&
1268 t <= v->coord.bottom &&
1269 r >= v->coord.left &&
1270 b >= v->coord.top) {
1271 DoDrawVehicle (vd, v);
1273 v = v->hash_viewport_link.next;
1276 } while (iter.next());
1280 * Find the vehicle close to the clicked coordinates.
1281 * @param x X coordinate in the viewport.
1282 * @param y Y coordinate in the viewport.
1283 * @return Closest vehicle, or \c NULL if none found.
1285 Vehicle *CheckClickOnVehicle (int x, int y)
1287 Vehicle *found = NULL, *v;
1288 uint dist, best_dist = UINT_MAX;
1290 FOR_ALL_VEHICLES(v) {
1291 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1292 x >= v->coord.left && x <= v->coord.right &&
1293 y >= v->coord.top && y <= v->coord.bottom) {
1295 dist = max(
1296 abs(((v->coord.left + v->coord.right) >> 1) - x),
1297 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1300 if (dist < best_dist) {
1301 found = v;
1302 best_dist = dist;
1307 return found;
1311 * Decrease the value of a vehicle.
1312 * @param v %Vehicle to devaluate.
1314 void DecreaseVehicleValue(Vehicle *v)
1316 v->value -= v->value >> 8;
1317 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1320 static const byte _breakdown_chance[64] = {
1321 3, 3, 3, 3, 3, 3, 3, 3,
1322 4, 4, 5, 5, 6, 6, 7, 7,
1323 8, 8, 9, 9, 10, 10, 11, 11,
1324 12, 13, 13, 13, 13, 14, 15, 16,
1325 17, 19, 21, 25, 28, 31, 34, 37,
1326 40, 44, 48, 52, 56, 60, 64, 68,
1327 72, 80, 90, 100, 110, 120, 130, 140,
1328 150, 170, 190, 210, 230, 250, 250, 250,
1331 void CheckVehicleBreakdown(Vehicle *v)
1333 int rel, rel_old;
1335 /* decrease reliability */
1336 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1337 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1339 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1340 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1341 v->cur_speed < 5 || _game_mode == GM_MENU) {
1342 return;
1345 uint32 r = Random();
1347 /* increase chance of failure */
1348 int chance = v->breakdown_chance + 1;
1349 if (Chance16I(1, 25, r)) chance += 25;
1350 v->breakdown_chance = min(255, chance);
1352 /* calculate reliability value to use in comparison */
1353 rel = v->reliability;
1354 if (v->type == VEH_SHIP) rel += 0x6666;
1356 /* reduced breakdowns? */
1357 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1359 /* check if to break down */
1360 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1361 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1362 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1363 v->breakdown_chance = 0;
1368 * Handle all of the aspects of a vehicle breakdown
1369 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1370 * @return true iff the vehicle is stopped because of a breakdown
1371 * @note This function always returns false for aircraft, since these never stop for breakdowns
1373 bool Vehicle::HandleBreakdown()
1375 /* Possible states for Vehicle::breakdown_ctr
1376 * 0 - vehicle is running normally
1377 * 1 - vehicle is currently broken down
1378 * 2 - vehicle is going to break down now
1379 * >2 - vehicle is counting down to the actual breakdown event */
1380 switch (this->breakdown_ctr) {
1381 case 0:
1382 return false;
1384 case 2:
1385 this->breakdown_ctr = 1;
1387 if (this->breakdowns_since_last_service != 255) {
1388 this->breakdowns_since_last_service++;
1391 if (this->type == VEH_AIRCRAFT) {
1392 /* Aircraft just need this flag, the rest is handled elsewhere */
1393 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1394 } else {
1395 this->cur_speed = 0;
1397 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1398 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1399 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1400 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1401 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1404 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1405 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1406 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1410 this->MarkDirty(); // Update graphics after speed is zeroed
1411 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1412 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1414 /* FALL THROUGH */
1415 case 1:
1416 /* Aircraft breakdowns end only when arriving at the airport */
1417 if (this->type == VEH_AIRCRAFT) return false;
1419 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1420 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1421 if (--this->breakdown_delay == 0) {
1422 this->breakdown_ctr = 0;
1423 this->MarkDirty();
1424 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1427 return true;
1429 default:
1430 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1431 return false;
1436 * Update age of a vehicle.
1437 * @param v Vehicle to update.
1439 void AgeVehicle(Vehicle *v)
1441 if (v->age < MAX_DAY) {
1442 v->age++;
1443 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1446 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1448 int age = v->age - v->max_age;
1449 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1450 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1451 v->reliability_spd_dec <<= 1;
1454 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1456 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1457 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1459 /* Don't warn if a renew is active */
1460 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1462 StringID str;
1463 if (age == -DAYS_IN_LEAP_YEAR) {
1464 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1465 } else if (age == 0) {
1466 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1467 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1468 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1469 } else {
1470 return;
1473 AddNewsItem<VehicleAdviceNewsItem> (str, v->index);
1477 * Calculates how full a vehicle is.
1478 * @param front The front vehicle of the consist to check.
1479 * @param colour The string to show depending on if we are unloading or loading
1480 * @return A percentage of how full the Vehicle is.
1481 * Rounding is done in such a way that 0% and 100% are only returned
1482 * if the vehicle is completely empty or full, respectively.
1483 * This is useful for both display and conditional orders.
1485 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1487 bool is_loading = front->current_order.IsType(OT_LOADING);
1489 /* The station may be NULL when the (colour) string does not need to be set. */
1490 const Station *st = Station::GetIfValid(front->last_station_visited);
1491 assert(colour == NULL || (st != NULL && is_loading));
1493 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1494 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1496 /* Count up max and used */
1497 uint count = 0; // Total cargo count
1498 uint max = 0; // Total capacity
1499 bool loading = false; // Any vehicle is loading
1500 bool unloading = false; // Any vehicle is unloading
1501 bool all_unloading = true; // All vehicles are unloading, if any is
1502 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1503 count += v->cargo.StoredCount();
1504 max += v->cargo_cap;
1505 if (v->cargo_cap != 0 && colour != NULL) {
1506 if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
1507 unloading = true;
1508 } else {
1509 all_unloading = false;
1512 loading |= !order_no_load &&
1513 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1514 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1518 if (colour != NULL) {
1519 if (!unloading) {
1520 *colour = loading ? STR_PERCENT_UP : STR_PERCENT_NONE;
1521 } else if (all_unloading || !loading) {
1522 *colour = STR_PERCENT_DOWN;
1523 } else {
1524 *colour = STR_PERCENT_UP_DOWN;
1528 /* Train without capacity */
1529 if (max == 0) return 100;
1531 /* Compute and return the percentage */
1533 /* Correction to use for the percentage.
1534 * It is an affine, decreasing function on count that satisfies:
1535 * k == max - 1 if count == 0
1536 * k == 0 if count == max
1537 * max - count - 1 <= k <= max - count
1539 uint k = ((max - count) * (max - 1)) / max;
1541 /* Percentage to return.
1542 * It is an affine, increasing function on count that satisfies:
1543 * p == 0 iff count == 0
1544 * p == 100 iff count == max
1546 return (100 * count + k) / max;
1550 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1551 * @param v Vehicle that entered a depot.
1553 void VehicleEnterDepot(Vehicle *v)
1555 /* Always work with the front of the vehicle */
1556 assert(v == v->First());
1558 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1560 if (v->type != VEH_TRAIN) {
1561 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1562 * 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 */
1563 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1565 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1567 v->vehstatus |= VS_HIDDEN;
1568 v->cur_speed = 0;
1570 VehicleServiceInDepot(v);
1572 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1573 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1574 v->MarkDirty();
1576 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1577 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1579 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1581 /* Test whether we are heading for this depot. If not, do nothing.
1582 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1583 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1584 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1585 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1586 /* We are heading for another depot, keep driving. */
1587 return;
1590 if (v->current_order.IsRefit()) {
1591 assert (!v->current_order.IsAutoRefit());
1593 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1594 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, CMD_REFIT_VEHICLE);
1595 cur_company.Restore();
1597 if (cost.Failed()) {
1598 _vehicles_to_autoreplace[v] = false;
1599 if (v->owner == _local_company) {
1600 /* Notify the user that we stopped the vehicle */
1601 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_ORDER_REFIT_FAILED, v->index);
1603 } else if (cost.GetCost() != 0) {
1604 v->profit_this_year -= cost.GetCost() << 8;
1605 if (v->owner == _local_company) {
1606 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1611 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1612 /* Part of orders */
1613 v->DeleteUnreachedImplicitOrders();
1614 UpdateVehicleTimetable(v, true);
1615 v->IncrementImplicitOrderIndex();
1617 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1618 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1619 _vehicles_to_autoreplace[v] = false;
1620 /* Invalidate last_loading_station. As the link from the station
1621 * before the stop to the station after the stop can't be predicted
1622 * we shouldn't construct it when the vehicle visits the next stop. */
1623 v->last_loading_station = INVALID_STATION;
1624 if (v->owner == _local_company) {
1625 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1627 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1629 v->current_order.MakeDummy();
1635 * Update the position of the vehicle. This will update the hash that tells
1636 * which vehicles are on a tile.
1638 void Vehicle::UpdatePosition()
1640 vehicle_tile_hash.update (this);
1644 * Update the vehicle on the viewport, updating the right hash and setting the
1645 * new coordinates.
1646 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1648 void Vehicle::UpdateViewport(bool dirty)
1650 Rect new_coord;
1651 this->sprite_seq.GetBounds(&new_coord);
1653 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1654 new_coord.left += pt.x;
1655 new_coord.top += pt.y;
1656 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1657 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1659 vehicle_viewport_hash.update (this, new_coord.left, new_coord.top);
1661 Rect old_coord = this->coord;
1662 this->coord = new_coord;
1664 if (dirty) {
1665 if (old_coord.left == INVALID_COORD) {
1666 this->MarkAllViewportsDirty();
1667 } else {
1668 ::MarkAllViewportsDirty(
1669 min(old_coord.left, this->coord.left),
1670 min(old_coord.top, this->coord.top),
1671 max(old_coord.right, this->coord.right),
1672 max(old_coord.bottom, this->coord.bottom));
1678 * Update the position of the vehicle, and update the viewport.
1680 void Vehicle::UpdatePositionAndViewport()
1682 this->UpdatePosition();
1683 this->UpdateViewport(true);
1687 * Marks viewports dirty where the vehicle's image is.
1689 void Vehicle::MarkAllViewportsDirty() const
1691 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1695 * Get position information of a vehicle when moving one pixel in the direction it is facing
1696 * @param v Vehicle to move
1697 * @return Position information after the move
1699 FullPosTile GetNewVehiclePos(const Vehicle *v)
1701 FullPosTile gp;
1702 gp.set_towards (v->x_pos, v->y_pos, v->direction);
1703 return gp;
1706 static const Direction _new_direction_table[] = {
1707 DIR_N, DIR_NW, DIR_W,
1708 DIR_NE, DIR_SE, DIR_SW,
1709 DIR_E, DIR_SE, DIR_S
1712 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1714 int i = 0;
1716 if (y >= v->y_pos) {
1717 if (y != v->y_pos) i += 3;
1718 i += 3;
1721 if (x >= v->x_pos) {
1722 if (x != v->x_pos) i++;
1723 i++;
1726 Direction dir = v->direction;
1728 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1729 if (dirdiff == DIRDIFF_SAME) return dir;
1730 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1734 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1735 * struct initialization, except after each call to this->NextID() the returned value
1736 * is assigned to a vehicle.
1737 * @param type type of vehicle
1738 * @param owner owner of vehicles
1740 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1742 /* Find maximum */
1743 const Vehicle *v;
1744 FOR_ALL_VEHICLES(v) {
1745 if (v->type == type && v->owner == owner) {
1746 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1750 if (this->maxid == 0) return;
1752 /* Reserving 'maxid + 2' because we need:
1753 * - space for the last item (with v->unitnumber == maxid)
1754 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1755 this->cache = xcalloct<bool>(this->maxid + 2);
1757 /* Fill the cache */
1758 FOR_ALL_VEHICLES(v) {
1759 if (v->type == type && v->owner == owner) {
1760 this->cache[v->unitnumber] = true;
1765 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1766 UnitID FreeUnitIDGenerator::NextID()
1768 if (this->maxid <= this->curid) return ++this->curid;
1770 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1772 return this->curid;
1776 * Get an unused unit number for a vehicle (if allowed).
1777 * @param type Type of vehicle
1778 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1780 UnitID GetFreeUnitNumber(VehicleType type)
1782 /* Check whether it is allowed to build another vehicle. */
1783 uint max_veh;
1784 switch (type) {
1785 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1786 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1787 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1788 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1789 default: NOT_REACHED();
1792 const Company *c = Company::Get(_current_company);
1793 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1795 FreeUnitIDGenerator gen(type, _current_company);
1797 return gen.NextID();
1802 * Check whether we can build infrastructure for the given
1803 * vehicle type. This to disable building stations etc. when
1804 * you are not allowed/able to have the vehicle type yet.
1805 * @param type the vehicle type to check this for
1806 * @return true if there is any reason why you may build
1807 * the infrastructure for the given vehicle type
1809 bool CanBuildVehicleInfrastructure(VehicleType type)
1811 assert(IsCompanyBuildableVehicleType(type));
1813 if (!Company::IsValidID(_local_company)) return false;
1814 if (!_settings_client.gui.disable_unsuitable_building) return true;
1816 UnitID max;
1817 switch (type) {
1818 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1819 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1820 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1821 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1822 default: NOT_REACHED();
1825 /* We can build vehicle infrastructure when we may build the vehicle type */
1826 if (max > 0) {
1827 /* Can we actually build the vehicle type? */
1828 const Engine *e;
1829 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1830 if (HasBit(e->company_avail, _local_company)) return true;
1832 return false;
1835 /* We should be able to build infrastructure when we have the actual vehicle type */
1836 const Vehicle *v;
1837 FOR_ALL_VEHICLES(v) {
1838 if (v->owner == _local_company && v->type == type) return true;
1841 return false;
1846 * Determines the #LiveryScheme for a vehicle.
1847 * @param engine_type Engine of the vehicle.
1848 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1849 * @param v the vehicle, \c NULL if in purchase list etc.
1850 * @return livery scheme to use.
1852 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1854 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1855 const Engine *e = Engine::Get(engine_type);
1856 switch (e->type) {
1857 default: NOT_REACHED();
1858 case VEH_TRAIN:
1859 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1860 /* Wagonoverrides use the colour scheme of the front engine.
1861 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1862 engine_type = parent_engine_type;
1863 e = Engine::Get(engine_type);
1864 /* Note: Luckily cargo_type is not needed for engines */
1867 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1868 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1869 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1870 if (!CargoSpec::Get(cargo_type)->is_freight) {
1871 if (parent_engine_type == INVALID_ENGINE) {
1872 return LS_PASSENGER_WAGON_STEAM;
1873 } else {
1874 switch (RailVehInfo(parent_engine_type)->engclass) {
1875 default: NOT_REACHED();
1876 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1877 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1878 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1879 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1880 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1883 } else {
1884 return LS_FREIGHT_WAGON;
1886 } else {
1887 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1889 switch (e->u.rail.engclass) {
1890 default: NOT_REACHED();
1891 case EC_STEAM: return LS_STEAM;
1892 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1893 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1894 case EC_MONORAIL: return LS_MONORAIL;
1895 case EC_MAGLEV: return LS_MAGLEV;
1899 case VEH_ROAD:
1900 /* Always use the livery of the front */
1901 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1902 engine_type = parent_engine_type;
1903 e = Engine::Get(engine_type);
1904 cargo_type = v->First()->cargo_type;
1906 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1907 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1909 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1910 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1911 /* Tram */
1912 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1913 } else {
1914 /* Bus or truck */
1915 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1918 case VEH_SHIP:
1919 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1920 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1921 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1923 case VEH_AIRCRAFT:
1924 switch (e->u.air.subtype) {
1925 case AIR_HELI: return LS_HELICOPTER;
1926 case AIR_CTOL: return LS_SMALL_PLANE;
1927 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1928 default: NOT_REACHED();
1934 * Determines the livery for a vehicle.
1935 * @param engine_type EngineID of the vehicle
1936 * @param company Owner of the vehicle
1937 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1938 * @param v the vehicle. NULL if in purchase list etc.
1939 * @param livery_setting The livery settings to use for acquiring the livery information.
1940 * @return livery to use
1942 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1944 const Company *c = Company::Get(company);
1945 LiveryScheme scheme = LS_DEFAULT;
1947 /* The default livery is always available for use, but its in_use flag determines
1948 * whether any _other_ liveries are in use. */
1949 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1950 /* Determine the livery scheme to use */
1951 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1953 /* Switch back to the default scheme if the resolved scheme is not in use */
1954 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1957 return &c->livery[scheme];
1961 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1963 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1965 /* Return cached value if any */
1966 if (map != PAL_NONE) return map;
1968 const Engine *e = Engine::Get(engine_type);
1970 /* Check if we should use the colour map callback */
1971 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1972 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1973 /* Failure means "use the default two-colour" */
1974 if (callback != CALLBACK_FAILED) {
1975 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1976 map = GB(callback, 0, 14);
1977 /* If bit 14 is set, then the company colours are applied to the
1978 * map else it's returned as-is. */
1979 if (!HasBit(callback, 14)) {
1980 /* Update cache */
1981 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1982 return map;
1987 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1989 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1991 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1992 if (!Company::IsValidID(company)) return map;
1994 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1996 map += livery->colour1;
1997 if (twocc) map += livery->colour2 * 16;
1999 /* Update cache */
2000 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
2001 return map;
2005 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2006 * @param engine_type ID of engine
2007 * @param company ID of company
2008 * @return A ready-to-use palette modifier
2010 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2012 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
2016 * Get the colour map for a vehicle.
2017 * @param v Vehicle to get colour map for
2018 * @return A ready-to-use palette modifier
2020 PaletteID GetVehiclePalette(const Vehicle *v)
2022 EngineID parent = v->IsGroundVehicle() ?
2023 GroundVehicleBase::From(v)->gcache.first_engine :
2024 INVALID_ENGINE;
2026 return GetEngineColourMap (v->engine_type, v->owner, parent, v);
2030 * Delete all implicit orders which were not reached.
2032 void Vehicle::DeleteUnreachedImplicitOrders()
2034 if (this->IsGroundVehicle()) {
2035 GroundVehicleBase *gv = GroundVehicleBase::From (this);
2036 if (HasBit(gv->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2037 /* Do not delete orders, only skip them */
2038 ClrBit(gv->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2039 this->cur_implicit_order_index = this->cur_real_order_index;
2040 InvalidateVehicleOrder(this, 0);
2041 return;
2045 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2046 while (order != NULL) {
2047 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2049 if (order->IsType(OT_IMPLICIT)) {
2050 DeleteOrder(this, this->cur_implicit_order_index);
2051 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2052 order = this->GetOrder(this->cur_implicit_order_index);
2053 } else {
2054 /* Skip non-implicit orders, e.g. service-orders */
2055 order = order->next;
2056 this->cur_implicit_order_index++;
2059 /* Wrap around */
2060 if (order == NULL) {
2061 order = this->GetOrder(0);
2062 this->cur_implicit_order_index = 0;
2068 * Prepare everything to begin the loading when arriving at a station.
2069 * @pre IsStationTile(this->tile) || this->type == VEH_SHIP.
2071 void Vehicle::BeginLoading()
2073 assert(IsStationTile(this->tile) || this->type == VEH_SHIP);
2075 if (this->current_order.IsType(OT_GOTO_STATION) &&
2076 this->current_order.GetDestination() == this->last_station_visited) {
2077 this->DeleteUnreachedImplicitOrders();
2079 /* Now both order indices point to the destination station, and we can start loading */
2080 this->current_order.MakeLoading(true);
2081 UpdateVehicleTimetable(this, true);
2083 /* Furthermore add the Non Stop flag to mark that this station
2084 * is the actual destination of the vehicle, which is (for example)
2085 * necessary to be known for HandleTrainLoading to determine
2086 * whether the train is lost or not; not marking a train lost
2087 * that arrives at random stations is bad. */
2088 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2090 } else {
2091 /* We weren't scheduled to stop here. Insert an implicit order
2092 * to show that we are stopping here.
2093 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2094 * the 'wrong' terminal when skipping orders etc. */
2095 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2096 if (this->IsGroundVehicle() &&
2097 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2098 in_list->GetDestination() != this->last_station_visited)) {
2099 bool suppress_implicit_orders = HasBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2100 /* Do not create consecutive duplicates of implicit orders */
2101 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2102 if (prev_order == NULL ||
2103 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2104 prev_order->GetDestination() != this->last_station_visited) {
2106 /* Prefer deleting implicit orders instead of inserting new ones,
2107 * so test whether the right order follows later. In case of only
2108 * implicit orders treat the last order in the list like an
2109 * explicit one, except if the overall number of orders surpasses
2110 * IMPLICIT_ORDER_ONLY_CAP. */
2111 int target_index = this->cur_implicit_order_index;
2112 bool found = false;
2113 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2114 const Order *order = this->GetOrder(target_index);
2115 if (order == NULL) break; // No orders.
2116 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2117 found = true;
2118 break;
2120 target_index++;
2121 if (target_index >= this->orders.list->GetNumOrders()) {
2122 if (this->GetNumManualOrders() == 0 &&
2123 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2124 break;
2126 target_index = 0;
2128 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2131 if (found) {
2132 if (suppress_implicit_orders) {
2133 /* Skip to the found order */
2134 this->cur_implicit_order_index = target_index;
2135 InvalidateVehicleOrder(this, 0);
2136 } else {
2137 /* Delete all implicit orders up to the station we just reached */
2138 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2139 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2140 if (order->IsType(OT_IMPLICIT)) {
2141 DeleteOrder(this, this->cur_implicit_order_index);
2142 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2143 order = this->GetOrder(this->cur_implicit_order_index);
2144 } else {
2145 /* Skip non-implicit orders, e.g. service-orders */
2146 order = order->next;
2147 this->cur_implicit_order_index++;
2150 /* Wrap around */
2151 if (order == NULL) {
2152 order = this->GetOrder(0);
2153 this->cur_implicit_order_index = 0;
2155 assert(order != NULL);
2158 } else if (!suppress_implicit_orders &&
2159 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2160 Order::CanAllocateItem()) {
2161 /* Insert new implicit order */
2162 Order *implicit_order = new Order();
2163 implicit_order->MakeImplicit(this->last_station_visited);
2164 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2165 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2167 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2168 * Reenable it for this vehicle */
2169 ClrBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2173 this->current_order.MakeLoading(false);
2176 if (this->last_loading_station != INVALID_STATION &&
2177 this->last_loading_station != this->last_station_visited &&
2178 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2179 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2180 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2183 PrepareUnload(this);
2185 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2186 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2187 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2188 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2190 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2191 this->cur_speed = 0;
2192 this->MarkDirty();
2196 * Return all reserved cargo packets to the station and reset all packets
2197 * staged for transfer.
2198 * @param st the station where the reserved packets should go.
2200 void Vehicle::CancelReservation(StationID next, Station *st)
2202 for (Vehicle *v = this; v != NULL; v = v->next) {
2203 VehicleCargoList &cargo = v->cargo;
2204 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2205 DEBUG(misc, 1, "cancelling cargo reservation");
2206 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2207 cargo.SetTransferLoadPlace(st->xy);
2209 cargo.KeepAll();
2214 * Perform all actions when leaving a station.
2215 * @pre this->current_order.IsType(OT_LOADING)
2217 void Vehicle::LeaveStation()
2219 assert(this->current_order.IsType(OT_LOADING));
2221 delete this->cargo_payment;
2222 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2224 /* Only update the timetable if the vehicle was supposed to stop here. */
2225 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2227 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2228 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2229 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2230 /* Refresh next hop stats to make sure we've done that at least once
2231 * during the stop and that refit_cap == cargo_cap for each vehicle in
2232 * the consist. */
2233 this->ResetRefitCaps();
2234 LinkRefresher::Run(this);
2236 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2237 this->last_loading_station = this->last_station_visited;
2238 } else {
2239 /* if the vehicle couldn't load and had to unload or transfer everything
2240 * set the last loading station to invalid as it will leave empty. */
2241 this->last_loading_station = INVALID_STATION;
2245 this->current_order.MakeLeaveStation();
2246 Station *st = Station::Get(this->last_station_visited);
2247 this->CancelReservation(INVALID_STATION, st);
2248 st->loading_vehicles.remove(this);
2250 HideFillingPercent(&this->fill_percent_te_id);
2252 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2253 /* Trigger station animation (trains only) */
2254 if (IsStationTile(this->tile)) {
2255 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2256 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2259 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2262 this->MarkDirty();
2266 * Reset all refit_cap in the consist to cargo_cap.
2268 void Vehicle::ResetRefitCaps()
2270 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2274 * Handle the loading of the vehicle; when not it skips through dummy
2275 * orders and does nothing in all other cases.
2276 * @param mode is the non-first call for this vehicle in this tick?
2278 void Vehicle::HandleLoading(bool mode)
2280 switch (this->current_order.GetType()) {
2281 case OT_LOADING: {
2282 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2284 /* Not the first call for this tick, or still loading */
2285 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2287 this->PlayLeaveStationSound();
2289 this->LeaveStation();
2291 /* Only advance to next order if we just loaded at the current one */
2292 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2293 if (order == NULL ||
2294 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2295 order->GetDestination() != this->last_station_visited) {
2296 return;
2298 break;
2301 case OT_DUMMY: break;
2303 default: return;
2306 this->IncrementImplicitOrderIndex();
2310 * Send this vehicle to the depot using the given command(s).
2311 * @param flags the command flags (like execute and such).
2312 * @param command the command to execute.
2313 * @return the cost of the depot action.
2315 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2317 CommandCost ret = CheckOwnership(this->owner);
2318 if (ret.Failed()) return ret;
2320 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2321 if (this->IsStoppedInDepot()) return CMD_ERROR;
2323 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2324 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2325 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2326 /* We called with a different DEPOT_SERVICE setting.
2327 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2328 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2329 if (flags & DC_EXEC) {
2330 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2331 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2332 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2334 return CommandCost();
2337 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2338 if (flags & DC_EXEC) {
2339 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2340 * then skip to the next order; effectively cancelling this forced service */
2341 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2343 if (this->IsGroundVehicle()) {
2344 SetBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2347 this->current_order.MakeDummy();
2348 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2350 return CommandCost();
2353 TileIndex location;
2354 DestinationID destination;
2355 bool reverse;
2356 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};
2357 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2359 if (flags & DC_EXEC) {
2360 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2362 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2363 SetBit(GroundVehicleBase::From(this)->gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2366 this->dest_tile = location;
2367 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2368 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2369 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2371 /* If there is no depot in front, reverse automatically (trains only) */
2372 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2374 if (this->type == VEH_AIRCRAFT) {
2375 Aircraft *a = Aircraft::From(this);
2376 if (a->state == FLYING && a->targetairport != destination) {
2377 /* The aircraft is now heading for a different hangar than the next in the orders */
2378 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2379 AircraftNextAirportPos_and_Order(a);
2384 return CommandCost();
2389 * Update the cached visual effect.
2390 * @param allow_power_change true if the wagon-is-powered-state may change.
2392 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2394 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2395 const Engine *e = this->GetEngine();
2397 /* Evaluate properties */
2398 byte visual_effect;
2399 switch (e->type) {
2400 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2401 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2402 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2403 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2406 /* Check powered wagon / visual effect callback */
2407 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2408 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2410 if (callback != CALLBACK_FAILED) {
2411 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2413 callback = GB(callback, 0, 8);
2414 /* Avoid accidentally setting 'visual_effect' to the default value
2415 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2416 if (callback == VE_DEFAULT) {
2417 assert(HasBit(callback, VE_DISABLE_EFFECT));
2418 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2420 visual_effect = callback;
2424 /* Apply default values */
2425 if (visual_effect == VE_DEFAULT ||
2426 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2427 /* Only train engines have default effects.
2428 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2429 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2430 if (visual_effect == VE_DEFAULT) {
2431 visual_effect = 1 << VE_DISABLE_EFFECT;
2432 } else {
2433 SetBit(visual_effect, VE_DISABLE_EFFECT);
2435 } else {
2436 if (visual_effect == VE_DEFAULT) {
2437 /* Also set the offset */
2438 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2440 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2444 this->vcache.cached_vis_effect = visual_effect;
2446 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2447 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2448 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2452 static const int8 _vehicle_smoke_pos[8] = {
2453 1, 1, 1, 0, -1, -1, -1, 0
2457 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2458 * @param v Vehicle to create effects for.
2460 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2462 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2463 if (callback == CALLBACK_FAILED) return;
2465 uint count = GB(callback, 0, 2);
2466 bool auto_center = HasBit(callback, 13);
2467 bool auto_rotate = !HasBit(callback, 14);
2469 int8 l_center = 0;
2470 if (auto_center) {
2471 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2472 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2473 } else {
2474 /* For trains: Compute offset from vehicle position to sprite position */
2475 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2478 Direction l_dir = v->direction;
2479 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2480 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2482 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2483 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2485 for (uint i = 0; i < count; i++) {
2486 uint32 reg = GetRegister(0x100 + i);
2487 uint type = GB(reg, 0, 8);
2488 int8 x = GB(reg, 8, 8);
2489 int8 y = GB(reg, 16, 8);
2490 int8 z = GB(reg, 24, 8);
2492 if (auto_rotate) {
2493 int8 l = x;
2494 int8 t = y;
2495 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2496 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2499 if (type >= 0xF0) {
2500 switch (type) {
2501 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2502 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2503 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2504 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2505 default: break;
2512 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2513 * @pre this->IsPrimaryVehicle()
2515 void Vehicle::ShowVisualEffect() const
2517 assert(this->IsPrimaryVehicle());
2518 bool sound = false;
2520 /* Do not show any smoke when:
2521 * - vehicle smoke is disabled by the player
2522 * - the vehicle is slowing down or stopped (by the player)
2523 * - the vehicle is moving very slowly
2525 if (_settings_game.vehicle.smoke_amount == 0 ||
2526 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2527 this->cur_speed < 2) {
2528 return;
2531 /* Use the speed as limited by underground and orders. */
2532 uint max_speed = this->GetCurrentMaxSpeed();
2533 if (max_speed == 0) return;
2535 if (this->type == VEH_TRAIN) {
2536 const Train *t = Train::From(this);
2537 /* For trains, do not show any smoke when:
2538 * - the train is reversing
2539 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2541 if (HasBit(t->flags, VRF_REVERSING) ||
2542 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2543 t->cur_speed >= max_speed)) {
2544 return;
2548 const Vehicle *v = this;
2550 do {
2551 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2552 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2553 VisualEffectSpawnModel effect_model = VESM_NONE;
2554 if (advanced) {
2555 effect_offset = VE_OFFSET_CENTRE;
2556 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2557 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2558 } else {
2559 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2560 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2561 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2562 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2563 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2566 /* Show no smoke when:
2567 * - Smoke has been disabled for this vehicle
2568 * - The vehicle is not visible
2569 * - The vehicle is under a bridge
2570 * - The vehicle is on a depot tile
2571 * - The vehicle is on a tunnel tile
2572 * - The vehicle is a train engine that is currently unpowered */
2573 if (effect_model == VESM_NONE ||
2574 v->vehstatus & VS_HIDDEN ||
2575 HasBridgeAbove(v->tile) ||
2576 IsDepotTile(v->tile) ||
2577 IsTunnelTile(v->tile) ||
2578 (v->type == VEH_TRAIN &&
2579 !HasPowerOnRail(Train::From(v)->railtype, Train::From(v)->GetTrackRailType()))) {
2580 continue;
2583 EffectVehicleType evt = EV_END;
2584 switch (effect_model) {
2585 case VESM_STEAM:
2586 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2587 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2588 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2589 * REGULATION:
2590 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2591 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2592 evt = EV_STEAM_SMOKE;
2594 break;
2596 case VESM_DIESEL: {
2597 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2598 * when smoke emission stops.
2599 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2600 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2601 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2602 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2603 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2604 * maximum speed no diesel_smoke is emitted.
2605 * REGULATION:
2606 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2607 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2608 int power_weight_effect = 0;
2609 if (v->type == VEH_TRAIN) {
2610 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2612 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2613 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2614 evt = EV_DIESEL_SMOKE;
2616 break;
2619 case VESM_ELECTRIC:
2620 /* Electric train's spark - more often occurs when train is departing (more load)
2621 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2622 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2623 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2624 * REGULATION:
2625 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2626 if (GB(v->tick_counter, 0, 2) == 0 &&
2627 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2628 evt = EV_ELECTRIC_SPARK;
2630 break;
2632 default:
2633 NOT_REACHED();
2636 if (evt != EV_END && advanced) {
2637 sound = true;
2638 SpawnAdvancedVisualEffect(v);
2639 } else if (evt != EV_END) {
2640 sound = true;
2642 /* The effect offset is relative to a point 4 units behind the vehicle's
2643 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2644 * correction factor. */
2645 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2647 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2648 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2650 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2651 x = -x;
2652 y = -y;
2655 CreateEffectVehicleRel(v, x, y, 10, evt);
2657 } while ((v = v->Next()) != NULL);
2659 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2663 * Set the next vehicle of this vehicle.
2664 * @param next the next vehicle. NULL removes the next vehicle.
2666 void Vehicle::SetNext(Vehicle *next)
2668 assert(this != next);
2670 if (this->next != NULL) {
2671 /* We had an old next vehicle. Update the first and previous pointers */
2672 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2673 v->first = this->next;
2675 this->next->previous = NULL;
2678 this->next = next;
2680 if (this->next != NULL) {
2681 /* A new next vehicle. Update the first and previous pointers */
2682 if (this->next->previous != NULL) this->next->previous->next = NULL;
2683 this->next->previous = this;
2684 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2685 v->first = this->first;
2691 * Adds this vehicle to a shared vehicle chain.
2692 * @param shared_chain a vehicle of the chain with shared vehicles.
2693 * @pre !this->IsOrderListShared()
2695 void Vehicle::AddToShared(Vehicle *shared_chain)
2697 assert(this->previous_shared == NULL && this->next_shared == NULL);
2699 if (shared_chain->orders.list == NULL) {
2700 assert(shared_chain->previous_shared == NULL);
2701 assert(shared_chain->next_shared == NULL);
2702 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2705 this->next_shared = shared_chain->next_shared;
2706 this->previous_shared = shared_chain;
2708 shared_chain->next_shared = this;
2710 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2712 shared_chain->orders.list->AddVehicle(this);
2716 * Removes the vehicle from the shared order list.
2718 void Vehicle::RemoveFromShared()
2720 /* Remember if we were first and the old window number before RemoveVehicle()
2721 * as this changes first if needed. */
2722 bool were_first = (this->FirstShared() == this);
2723 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2725 this->orders.list->RemoveVehicle(this);
2727 if (!were_first) {
2728 /* We are not the first shared one, so only relink our previous one. */
2729 this->previous_shared->next_shared = this->NextShared();
2732 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2735 if (this->orders.list->GetNumVehicles() == 1) {
2736 /* When there is only one vehicle, remove the shared order list window. */
2737 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2738 InvalidateVehicleOrder(this->FirstShared(), 0);
2739 } else if (were_first) {
2740 /* If we were the first one, update to the new first one.
2741 * Note: FirstShared() is already the new first */
2742 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2745 this->next_shared = NULL;
2746 this->previous_shared = NULL;
2749 void VehiclesYearlyLoop()
2751 Vehicle *v;
2752 FOR_ALL_VEHICLES(v) {
2753 if (v->IsPrimaryVehicle()) {
2754 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2755 Money profit = v->GetDisplayProfitThisYear();
2756 if (v->age >= 730 && profit < 0) {
2757 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2758 AddNewsItem<VehicleAdviceNewsItem> (STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index, profit);
2760 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2763 v->profit_last_year = v->profit_this_year;
2764 v->profit_this_year = 0;
2765 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2768 GroupStatistics::UpdateProfits();
2769 SetWindowClassesDirty(WC_TRAINS_LIST);
2770 SetWindowClassesDirty(WC_SHIPS_LIST);
2771 SetWindowClassesDirty(WC_ROADVEH_LIST);
2772 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2777 * Can this station be used by the given engine type?
2778 * @param engine_type the type of vehicles to test
2779 * @param st the station to test for
2780 * @return true if and only if the vehicle of the type can use this station.
2781 * @note For road vehicles the Vehicle is needed to determine whether it can
2782 * use the station. This function will return true for road vehicles
2783 * when at least one of the facilities is available.
2785 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2787 const Engine *e = Engine::GetIfValid(engine_type);
2788 assert(e != NULL);
2790 switch (e->type) {
2791 case VEH_TRAIN:
2792 return (st->facilities & FACIL_TRAIN) != 0;
2794 case VEH_ROAD:
2795 /* For road vehicles we need the vehicle to know whether it can actually
2796 * use the station, but if it doesn't have facilities for RVs it is
2797 * certainly not possible that the station can be used. */
2798 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2800 case VEH_SHIP:
2801 return (st->facilities & FACIL_DOCK) != 0;
2803 case VEH_AIRCRAFT:
2804 return (st->facilities & FACIL_AIRPORT) != 0 &&
2805 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2807 default:
2808 return false;
2813 * Can this station be used by the given vehicle?
2814 * @param v the vehicle to test
2815 * @param st the station to test for
2816 * @return true if and only if the vehicle can use this station.
2818 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2820 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2822 return CanVehicleUseStation(v->engine_type, st);
2826 * Calculates the set of vehicles that will be affected by a given selection.
2827 * @param set [inout] Set of affected vehicles.
2828 * @param v First vehicle of the selection.
2829 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2830 * @pre \a set must be empty.
2831 * @post \a set will contain the vehicles that will be refitted.
2833 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2835 if (v->type == VEH_TRAIN) {
2836 Train *u = Train::From(v);
2837 /* Only include whole vehicles, so start with the first articulated part */
2838 u = u->GetFirstEnginePart();
2840 /* Include num_vehicles vehicles, not counting articulated parts */
2841 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2842 do {
2843 /* Include current vehicle in the selection. */
2844 set.Include(u->index);
2846 /* If the vehicle is multiheaded, add the other part too. */
2847 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2849 u = u->Next();
2850 } while (u != NULL && u->IsArticulatedPart());