Add a method in NewGRFInspectWindow to resolve FeatureIndex
[openttd/fttd.git] / src / vehicle.cpp
blobfca487ab5f3040d6277874956b7f0800d43c1a7f
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 "gamelog.h"
52 #include "signal_func.h"
53 #include "linkgraph/linkgraph.h"
54 #include "linkgraph/refresh.h"
56 #include "table/strings.h"
58 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
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 VehiclePool _vehicle_pool("Vehicle");
67 INSTANTIATE_POOL_METHODS(Vehicle)
69 /**
70 * Function to tell if a vehicle needs to be autorenewed
71 * @param *c The vehicle owner
72 * @param use_renew_setting Should the company renew setting be considered?
73 * @return true if the vehicle is old enough for replacement
75 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
77 /* We can always generate the Company pointer when we have the vehicle.
78 * However this takes time and since the Company pointer is often present
79 * when this function is called then it's faster to pass the pointer as an
80 * argument rather than finding it again. */
81 assert(c == Company::Get(this->owner));
83 if (use_renew_setting && !c->settings.engine_renew) return false;
84 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
86 /* Only engines need renewing */
87 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
89 return true;
92 /**
93 * Service a vehicle and all subsequent vehicles in the consist
95 * @param *v The vehicle or vehicle chain being serviced
97 void VehicleServiceInDepot(Vehicle *v)
99 assert(v != NULL);
100 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
102 do {
103 v->date_of_last_service = _date;
104 v->breakdowns_since_last_service = 0;
105 v->reliability = v->GetEngine()->reliability;
106 /* Prevent vehicles from breaking down directly after exiting the depot. */
107 v->breakdown_chance /= 4;
108 v = v->Next();
109 } while (v != NULL && v->HasEngineType());
113 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
115 * @see NeedsAutomaticServicing()
116 * @return true if the vehicle should go to a depot if a opportunity presents itself.
118 bool Vehicle::NeedsServicing() const
120 /* Stopped or crashed vehicles will not move, as such making unmovable
121 * vehicles to go for service is lame. */
122 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
124 /* Are we ready for the next service cycle? */
125 const Company *c = Company::Get(this->owner);
126 if (this->ServiceIntervalIsPercent() ?
127 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
128 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
129 return false;
132 /* If we're servicing anyway, because we have not disabled servicing when
133 * there are no breakdowns or we are playing with breakdowns, bail out. */
134 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
135 _settings_game.difficulty.vehicle_breakdowns != 0) {
136 return true;
139 /* Test whether there is some pending autoreplace.
140 * Note: We do this after the service-interval test.
141 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
142 bool pending_replace = false;
143 Money needed_money = c->settings.engine_renew_money;
144 if (needed_money > c->money) return false;
146 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
147 bool replace_when_old = false;
148 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
150 /* Check engine availability */
151 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
152 /* Is the vehicle old if we are not always replacing? */
153 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
155 /* Check refittability */
156 uint32 available_cargo_types, union_mask;
157 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
158 /* Is there anything to refit? */
159 if (union_mask != 0) {
160 CargoID cargo_type;
161 /* We cannot refit to mixed cargoes in an automated way */
162 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
164 /* Did the old vehicle carry anything? */
165 if (cargo_type != CT_INVALID) {
166 /* We can't refit the vehicle to carry the cargo we want */
167 if (!HasBit(available_cargo_types, cargo_type)) continue;
171 /* Check money.
172 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
173 pending_replace = true;
174 needed_money += 2 * Engine::Get(new_engine)->GetCost();
175 if (needed_money > c->money) return false;
178 return pending_replace;
182 * Checks if the current order should be interrupted for a service-in-depot order.
183 * @see NeedsServicing()
184 * @return true if the current order should be interrupted.
186 bool Vehicle::NeedsAutomaticServicing() const
188 if (this->HasDepotOrder()) return false;
189 if (this->current_order.IsType(OT_LOADING)) return false;
190 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
191 return NeedsServicing();
194 uint Vehicle::Crash(bool flooded)
196 assert((this->vehstatus & VS_CRASHED) == 0);
197 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
199 uint pass = 0;
200 /* Stop the vehicle. */
201 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
202 /* crash all wagons, and count passengers */
203 for (Vehicle *v = this; v != NULL; v = v->Next()) {
204 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
205 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
206 v->vehstatus |= VS_CRASHED;
207 MarkSingleVehicleDirty(v);
210 /* Dirty some windows */
211 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
212 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
213 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
214 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
216 delete this->cargo_payment;
217 this->cargo_payment = NULL;
219 return RandomRange(pass + 1); // Randomise deceased passengers.
224 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
225 * @param engine The engine that caused the problem
226 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
227 * @param part2 Part 2 of the error message, taking the engine as parameter 2
228 * @param bug_type Flag to check and set in grfconfig
229 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
231 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
233 const Engine *e = Engine::Get(engine);
234 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
236 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
237 SetBit(grfconfig->grf_bugs, bug_type);
238 SetDParamStr(0, grfconfig->GetName());
239 SetDParam(1, engine);
240 ShowErrorMessage(part1, part2, WL_CRITICAL);
241 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
244 /* debug output */
245 char buffer[512];
247 SetDParamStr(0, grfconfig->GetName());
248 GetString(buffer, part1, lastof(buffer));
249 DEBUG(grf, 0, "%s", buffer + 3);
251 SetDParam(1, engine);
252 GetString(buffer, part2, lastof(buffer));
253 DEBUG(grf, 0, "%s", buffer + 3);
257 * Logs a bug in GRF and shows a warning message if this
258 * is for the first time this happened.
259 * @param u first vehicle of chain
261 void VehicleLengthChanged(const Vehicle *u)
263 /* show a warning once for each engine in whole game and once for each GRF after each game load */
264 const Engine *engine = u->GetEngine();
265 uint32 grfid = engine->grf_prop.grffile->grfid;
266 GRFConfig *grfconfig = GetGRFConfig(grfid);
267 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
268 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
273 * Vehicle constructor.
274 * @param type Type of the new vehicle.
276 Vehicle::Vehicle(VehicleType type)
278 this->type = type;
279 this->coord.left = INVALID_COORD;
280 this->group_id = DEFAULT_GROUP;
281 this->fill_percent_te_id = INVALID_TE_ID;
282 this->first = this;
283 this->colourmap = PAL_NONE;
284 this->cargo_age_counter = 1;
285 this->last_station_visited = INVALID_STATION;
286 this->last_loading_station = INVALID_STATION;
290 * Get a value for a vehicle's random_bits.
291 * @return A random value from 0 to 255.
293 byte VehicleRandomBits()
295 return GB(Random(), 0, 8);
298 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
299 * lookup times at the expense of memory usage. */
300 const int HASH_BITS = 7;
301 const int HASH_SIZE = 1 << HASH_BITS;
302 const int HASH_MASK = HASH_SIZE - 1;
303 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
304 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
306 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
307 * Profiling results show that 0 is fastest. */
308 const int HASH_RES = 0;
310 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
312 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
314 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
315 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
316 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
317 for (; v != NULL; v = v->hash_tile_next) {
318 Vehicle *a = proc(v, data);
319 if (find_first && a != NULL) return a;
321 if (x == xu) break;
323 if (y == yu) break;
326 return NULL;
331 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
332 * @note Do not call this function directly!
333 * @param x The X location on the map
334 * @param y The Y location on the map
335 * @param data Arbitrary data passed to proc
336 * @param proc The proc that determines whether a vehicle will be "found".
337 * @param find_first Whether to return on the first found or iterate over
338 * all vehicles
339 * @return the best matching or first vehicle (depending on find_first).
341 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
343 const int COLL_DIST = 6;
345 /* Hash area to scan is from xl,yl to xu,yu */
346 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
347 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
348 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
349 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
351 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
355 * Find a vehicle from a specific location. It will call proc for ALL vehicles
356 * on the tile and YOU must make SURE that the "best one" is stored in the
357 * data value and is ALWAYS the same regardless of the order of the vehicles
358 * where proc was called on!
359 * When you fail to do this properly you create an almost untraceable DESYNC!
360 * @note The return value of proc will be ignored.
361 * @note Use this when you have the intention that all vehicles
362 * should be iterated over.
363 * @param x The X location on the map
364 * @param y The Y location on the map
365 * @param data Arbitrary data passed to proc
366 * @param proc The proc that determines whether a vehicle will be "found".
368 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
370 VehicleFromPosXY(x, y, data, proc, false);
374 * Checks whether a vehicle in on a specific location. It will call proc for
375 * vehicles until it returns non-NULL.
376 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
377 * should be iterated over.
378 * @param x The X location on the map
379 * @param y The Y location on the map
380 * @param data Arbitrary data passed to proc
381 * @param proc The proc that determines whether a vehicle will be "found".
382 * @return True if proc returned non-NULL.
384 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
386 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
390 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
391 * @note Do not call this function directly!
392 * @param tile The location on the map
393 * @param data Arbitrary data passed to \a proc.
394 * @param proc The proc that determines whether a vehicle will be "found".
395 * @param find_first Whether to return on the first found or iterate over
396 * all vehicles
397 * @return the best matching or first vehicle (depending on find_first).
399 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
401 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
402 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
404 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
405 for (; v != NULL; v = v->hash_tile_next) {
406 if (v->tile != tile) continue;
408 Vehicle *a = proc(v, data);
409 if (find_first && a != NULL) return a;
412 return NULL;
416 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
417 * on the tile and YOU must make SURE that the "best one" is stored in the
418 * data value and is ALWAYS the same regardless of the order of the vehicles
419 * where proc was called on!
420 * When you fail to do this properly you create an almost untraceable DESYNC!
421 * @note The return value of \a proc will be ignored.
422 * @note Use this function when you have the intention that all vehicles
423 * should be iterated over.
424 * @param tile The location on the map
425 * @param data Arbitrary data passed to \a proc.
426 * @param proc The proc that determines whether a vehicle will be "found".
428 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
430 VehicleFromPos(tile, data, proc, false);
434 * Checks whether a vehicle is on a specific location. It will call \a proc for
435 * vehicles until it returns non-NULL.
436 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
437 * should be iterated over.
438 * @param tile The location on the map
439 * @param data Arbitrary data passed to \a proc.
440 * @param proc The \a proc that determines whether a vehicle will be "found".
441 * @return True if proc returned non-NULL.
443 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
445 return VehicleFromPos(tile, data, proc, true) != NULL;
449 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
450 * @param v Vehicle to examine.
451 * @param data Pointer to height data.
452 * @return \a v if conditions are met, else \c NULL.
454 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
456 int z = *(int*)data;
458 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
459 if (v->z_pos > z) return NULL;
461 return v;
465 * Ensure there is no vehicle at the ground at the given position.
466 * @param tile Position to examine.
467 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
469 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
471 int z = GetTileMaxPixelZ(tile);
473 /* Value v is not safe in MP games, however, it is used to generate a local
474 * error message only (which may be different for different machines).
475 * Such a message does not affect MP synchronisation.
477 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
478 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
479 return CommandCost();
482 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
483 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
485 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
486 if (v == (const Vehicle *)data) return NULL;
488 return v;
492 * Finds vehicle in tunnel / bridge
493 * @param tile first end
494 * @param endtile second end
495 * @param ignore Ignore this vehicle when searching
496 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
498 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
500 /* Value v is not safe in MP games, however, it is used to generate a local
501 * error message only (which may be different for different machines).
502 * Such a message does not affect MP synchronisation.
504 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
505 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
507 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
508 return CommandCost();
511 static Track AllowedNonOverlappingTrack(TrackBits bits)
513 switch (bits) {
514 case TRACK_BIT_UPPER: return TRACK_LOWER;
515 case TRACK_BIT_LOWER: return TRACK_UPPER;
516 case TRACK_BIT_LEFT: return TRACK_RIGHT;
517 case TRACK_BIT_RIGHT: return TRACK_LEFT;
518 default: return INVALID_TRACK;
522 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
524 if (v->type != VEH_TRAIN) return NULL;
526 Trackdir trackdir = Train::From(v)->trackdir;
527 if (trackdir >= TRACKDIR_END) return NULL; // in wormhole or depot
529 Track allowed = *(Track *)data;
530 if (TrackdirToTrack(trackdir) != allowed) return v;
532 return NULL;
536 * Tests if a vehicle interacts with the specified track bits.
537 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
539 * @param tile The tile.
540 * @param track_bits The track bits.
541 * @return \c true if no train that interacts, is found. \c false if a train is found.
543 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
545 assert(track_bits != TRACK_BIT_NONE);
547 Track allowed = AllowedNonOverlappingTrack(track_bits);
549 if (HasVehicleOnPos(tile, &allowed, &EnsureNoTrainOnTrackProc)) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
550 return CommandCost();
553 static Vehicle *EnsureNoTrainOnBridgeTrackProc(Vehicle *v, void *data)
555 if (v->type != VEH_TRAIN) return NULL;
557 Track allowed = *(Track *)data;
558 Trackdir trackdir = Train::From(v)->trackdir;
559 if (TrackdirToTrack(trackdir) != allowed) return v;
561 return NULL;
565 * Tests if a train interacts with the specified track bits or is on the bridge middle part.
567 * @param tile1 one bridge end
568 * @param bits1 track bits on first bridge end
569 * @param tile2 the other bridge end
570 * @param bits2 track bits on second bridge end
571 * @return whether there is a train on the bridge tracks or middle part
573 CommandCost EnsureNoTrainOnBridgeTrackBits(TileIndex tile1, TrackBits bits1, TileIndex tile2, TrackBits bits2)
575 assert(bits1 != TRACK_BIT_NONE);
576 assert(bits2 != TRACK_BIT_NONE);
578 Track allowed = AllowedNonOverlappingTrack(bits1);
579 if (HasVehicleOnPos(tile1, &allowed, &EnsureNoTrainOnBridgeTrackProc)) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
581 allowed = AllowedNonOverlappingTrack(bits2);
582 if (HasVehicleOnPos(tile2, &allowed, &EnsureNoTrainOnBridgeTrackProc)) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
584 return CommandCost();
587 static Vehicle *EnsureNoTrainOnTunnelBridgeMiddleProc(Vehicle *v, void *data)
589 return (v->type == VEH_TRAIN) && (Train::From(v)->trackdir == TRACKDIR_WORMHOLE) ? v : NULL;
593 * Tests if there is a train on the middle bridge part
594 * @param tile1 one bridge end
595 * @param tile2 the other bridge end
596 * @return whether there is a train on the bridge
598 CommandCost EnsureNoTrainOnTunnelBridgeMiddle(TileIndex tile1, TileIndex tile2)
600 if (HasVehicleOnPos(tile1, NULL, &EnsureNoTrainOnTunnelBridgeMiddleProc)) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
601 if (HasVehicleOnPos(tile2, NULL, &EnsureNoTrainOnTunnelBridgeMiddleProc)) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY);
602 return CommandCost();
605 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
607 Vehicle **old_hash = v->hash_tile_current;
608 Vehicle **new_hash;
610 if (remove) {
611 new_hash = NULL;
612 } else {
613 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
614 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
615 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
618 if (old_hash == new_hash) return;
620 /* Remove from the old position in the hash table */
621 if (old_hash != NULL) {
622 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
623 *v->hash_tile_prev = v->hash_tile_next;
626 /* Insert vehicle at beginning of the new position in the hash table */
627 if (new_hash != NULL) {
628 v->hash_tile_next = *new_hash;
629 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
630 v->hash_tile_prev = new_hash;
631 *new_hash = v;
634 /* Remember current hash position */
635 v->hash_tile_current = new_hash;
638 static Vehicle *_vehicle_viewport_hash[0x1000];
640 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
642 Vehicle **old_hash, **new_hash;
643 int old_x = v->coord.left;
644 int old_y = v->coord.top;
646 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
647 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
649 if (old_hash == new_hash) return;
651 /* remove from hash table? */
652 if (old_hash != NULL) {
653 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
654 *v->hash_viewport_prev = v->hash_viewport_next;
657 /* insert into hash table? */
658 if (new_hash != NULL) {
659 v->hash_viewport_next = *new_hash;
660 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
661 v->hash_viewport_prev = new_hash;
662 *new_hash = v;
666 void ResetVehicleHash()
668 Vehicle *v;
669 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
670 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
671 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
674 void ResetVehicleColourMap()
676 Vehicle *v;
677 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
681 * List of vehicles that should check for autoreplace this tick.
682 * Mapping of vehicle -> leave depot immediately after autoreplace.
684 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
685 static AutoreplaceMap _vehicles_to_autoreplace;
687 void InitializeVehicles()
689 _vehicles_to_autoreplace.Reset();
690 ResetVehicleHash();
693 uint CountVehiclesInChain(const Vehicle *v)
695 uint count = 0;
696 do count++; while ((v = v->Next()) != NULL);
697 return count;
701 * Check if a vehicle is counted in num_engines in each company struct
702 * @return true if the vehicle is counted in num_engines
704 bool Vehicle::IsEngineCountable() const
706 switch (this->type) {
707 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
708 case VEH_TRAIN:
709 return !this->IsArticulatedPart() && // tenders and other articulated parts
710 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
711 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
712 case VEH_SHIP: return true;
713 default: return false; // Only count company buildable vehicles
718 * Check whether Vehicle::engine_type has any meaning.
719 * @return true if the vehicle has a useable engine type.
721 bool Vehicle::HasEngineType() const
723 switch (this->type) {
724 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
725 case VEH_TRAIN:
726 case VEH_ROAD:
727 case VEH_SHIP: return true;
728 default: return false;
733 * Retrieves the engine of the vehicle.
734 * @return Engine of the vehicle.
735 * @pre HasEngineType() == true
737 const Engine *Vehicle::GetEngine() const
739 return Engine::Get(this->engine_type);
743 * Retrieve the NewGRF the vehicle is tied to.
744 * This is the GRF providing the Action 3 for the engine type.
745 * @return NewGRF associated to the vehicle.
747 const GRFFile *Vehicle::GetGRF() const
749 return this->GetEngine()->GetGRF();
753 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
754 * This is the GRF providing the Action 3 for the engine type.
755 * @return GRF ID of the associated NewGRF.
757 uint32 Vehicle::GetGRFID() const
759 return this->GetEngine()->GetGRFID();
763 * Handle the pathfinding result, especially the lost status.
764 * If the vehicle is now lost and wasn't previously fire an
765 * event to the AIs and a news message to the user. If the
766 * vehicle is not lost anymore remove the news message.
767 * @param path_found Whether the vehicle has a path to its destination.
769 void Vehicle::HandlePathfindingResult(bool path_found)
771 if (path_found) {
772 /* Route found, is the vehicle marked with "lost" flag? */
773 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
775 /* Clear the flag as the PF's problem was solved. */
776 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
777 /* Delete the news item. */
778 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
779 return;
782 /* Were we already lost? */
783 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
785 /* It is first time the problem occurred, set the "lost" flag. */
786 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
787 /* Notify user about the event. */
788 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
789 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
790 SetDParam(0, this->index);
791 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
795 /** Destroy all stuff that (still) needs the virtual functions to work properly */
796 void Vehicle::PreDestructor()
798 if (CleaningPool()) return;
800 if (Station::IsValidID(this->last_station_visited)) {
801 Station *st = Station::Get(this->last_station_visited);
802 st->loading_vehicles.remove(this);
804 HideFillingPercent(&this->fill_percent_te_id);
805 this->CancelReservation(INVALID_STATION, st);
806 delete this->cargo_payment;
809 if (this->IsEngineCountable()) {
810 GroupStatistics::CountEngine(this, -1);
811 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
812 GroupStatistics::UpdateAutoreplace(this->owner);
814 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
815 DeleteGroupHighlightOfVehicle(this);
818 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
819 Aircraft *a = Aircraft::From(this);
820 Station *st = GetTargetAirportIfValid(a);
821 if (st != NULL) {
822 const AirportFTA *layout = st->airport.GetFTA()->layout;
823 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
828 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
829 RoadVehicle *v = RoadVehicle::From(this);
830 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
831 /* Leave the drive through roadstop, when you have not already left it. */
832 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
836 if (this->Previous() == NULL) {
837 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
840 if (this->IsPrimaryVehicle()) {
841 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
842 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
843 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
844 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
845 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
846 SetWindowDirty(WC_COMPANY, this->owner);
847 OrderBackup::ClearVehicle(this);
849 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
851 this->cargo.Truncate();
852 DeleteVehicleOrders(this);
853 DeleteDepotHighlightOfVehicle(this);
855 extern void StopGlobalFollowVehicle(const Vehicle *v);
856 StopGlobalFollowVehicle(this);
858 ReleaseDisastersTargetingVehicle(this->index);
861 Vehicle::~Vehicle()
863 if (CleaningPool()) {
864 this->cargo.OnCleanPool();
865 return;
868 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
869 * it may happen that vehicle chain is deleted when visible */
870 if (!(this->vehstatus & VS_HIDDEN)) MarkSingleVehicleDirty(this);
872 Vehicle *v = this->Next();
873 this->SetNext(NULL);
875 delete v;
877 UpdateVehicleTileHash(this, true);
878 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
879 DeleteVehicleNews(this->index, INVALID_STRING_ID);
880 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
884 * Adds a vehicle to the list of vehicles that visited a depot this tick
885 * @param *v vehicle to add
887 void VehicleEnteredDepotThisTick(Vehicle *v)
889 /* Vehicle should stop in the depot if it was in 'stopping' state */
890 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
892 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
893 * stopping in the depot, so we stop it to ensure that it will not reserve
894 * the path out of the depot before we might autoreplace it to a different
895 * engine. The new engine would not own the reserved path we store that we
896 * stopped the vehicle, so autoreplace can start it again */
897 v->vehstatus |= VS_STOPPED;
901 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
902 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
903 * so each day, all vehicles are processes in DAY_TICKS steps.
905 static void RunVehicleDayProc()
907 if (_game_mode != GM_NORMAL) return;
909 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
910 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
911 Vehicle *v = Vehicle::Get(i);
912 if (v == NULL) continue;
914 /* Call the 32-day callback if needed */
915 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
916 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
917 if (callback != CALLBACK_FAILED) {
918 if (HasBit(callback, 0)) {
919 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
922 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
923 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
924 if (callback != 0) v->First()->MarkDirty();
926 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
930 /* This is called once per day for each vehicle, but not in the first tick of the day */
931 v->OnNewDay();
935 void CallVehicleTicks()
937 _vehicles_to_autoreplace.Clear();
939 RunVehicleDayProc();
941 Station *st;
942 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
944 Vehicle *v;
945 FOR_ALL_VEHICLES(v) {
946 /* Vehicle could be deleted in this tick */
947 if (!v->Tick()) {
948 assert(Vehicle::Get(vehicle_index) == NULL);
949 continue;
952 assert(Vehicle::Get(vehicle_index) == v);
954 switch (v->type) {
955 default: break;
957 case VEH_TRAIN:
958 case VEH_ROAD:
959 case VEH_AIRCRAFT:
960 case VEH_SHIP: {
961 Vehicle *front = v->First();
963 if (v->vcache.cached_cargo_age_period != 0) {
964 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
965 if (--v->cargo_age_counter == 0) {
966 v->cargo.AgeCargo();
967 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
971 /* Do not play any sound when crashed */
972 if (front->vehstatus & VS_CRASHED) continue;
974 /* Do not play any sound when in depot or tunnel */
975 if (v->vehstatus & VS_HIDDEN) continue;
977 /* Do not play any sound when stopped */
978 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
980 /* Check vehicle type specifics */
981 switch (v->type) {
982 case VEH_TRAIN:
983 if (Train::From(v)->IsWagon()) continue;
984 break;
986 case VEH_ROAD:
987 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
988 break;
990 case VEH_AIRCRAFT:
991 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
992 break;
994 default:
995 break;
998 v->motion_counter += front->cur_speed;
999 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1000 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1002 /* Play an alternating running sound every 16 ticks */
1003 if (GB(v->tick_counter, 0, 4) == 0) {
1004 /* Play running sound when speed > 0 and not braking */
1005 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1006 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1009 break;
1014 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1015 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1016 v = it->first;
1017 /* Autoreplace needs the current company set as the vehicle owner */
1018 cur_company.Change(v->owner);
1020 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1021 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1022 * they are already leaving the depot again before being replaced. */
1023 if (it->second) v->vehstatus &= ~VS_STOPPED;
1025 /* Store the position of the effect as the vehicle pointer will become invalid later */
1026 int x = v->x_pos;
1027 int y = v->y_pos;
1028 int z = v->z_pos;
1030 const Company *c = Company::Get(_current_company);
1031 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1032 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1033 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1035 if (!IsLocalCompany()) continue;
1037 if (res.Succeeded()) {
1038 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1039 continue;
1042 StringID error_message = res.GetErrorMessage();
1043 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1045 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1047 StringID message;
1048 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1049 message = error_message;
1050 } else {
1051 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1054 SetDParam(0, v->index);
1055 SetDParam(1, error_message);
1056 AddVehicleAdviceNewsItem(message, v->index);
1059 cur_company.Restore();
1063 * Add vehicle sprite for drawing to the screen.
1064 * @param v Vehicle to draw.
1066 static void DoDrawVehicle(const Vehicle *v)
1068 SpriteID image = v->cur_image;
1069 PaletteID pal = PAL_NONE;
1071 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1073 /* Check whether the vehicle shall be transparent due to the game state */
1074 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1076 if (v->type == VEH_EFFECT) {
1077 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1078 * However, transparent smoke and bubbles look weird, so always hide them. */
1079 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1080 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1083 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1084 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1088 * Add the vehicle sprites that should be drawn at a part of the screen.
1089 * @param dpi Rectangle being drawn.
1091 void ViewportAddVehicles(DrawPixelInfo *dpi)
1093 /* The bounding rectangle */
1094 const int l = dpi->left;
1095 const int r = dpi->left + dpi->width;
1096 const int t = dpi->top;
1097 const int b = dpi->top + dpi->height;
1099 /* The hash area to scan */
1100 int xl, xu, yl, yu;
1102 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
1103 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
1104 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
1105 } else {
1106 /* scan whole hash row */
1107 xl = 0;
1108 xu = 0x3F;
1111 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
1112 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
1113 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
1114 } else {
1115 /* scan whole column */
1116 yl = 0;
1117 yu = 0x3F << 6;
1120 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1121 for (int x = xl;; x = (x + 1) & 0x3F) {
1122 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1124 while (v != NULL) {
1125 if (!(v->vehstatus & VS_HIDDEN) &&
1126 l <= v->coord.right &&
1127 t <= v->coord.bottom &&
1128 r >= v->coord.left &&
1129 b >= v->coord.top) {
1130 DoDrawVehicle(v);
1132 v = v->hash_viewport_next;
1135 if (x == xu) break;
1138 if (y == yu) break;
1143 * Find the vehicle close to the clicked coordinates.
1144 * @param vp Viewport clicked in.
1145 * @param x X coordinate in the viewport.
1146 * @param y Y coordinate in the viewport.
1147 * @return Closest vehicle, or \c NULL if none found.
1149 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1151 Vehicle *found = NULL, *v;
1152 uint dist, best_dist = UINT_MAX;
1154 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1156 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1157 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1159 FOR_ALL_VEHICLES(v) {
1160 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1161 x >= v->coord.left && x <= v->coord.right &&
1162 y >= v->coord.top && y <= v->coord.bottom) {
1164 dist = max(
1165 abs(((v->coord.left + v->coord.right) >> 1) - x),
1166 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1169 if (dist < best_dist) {
1170 found = v;
1171 best_dist = dist;
1176 return found;
1180 * Decrease the value of a vehicle.
1181 * @param v %Vehicle to devaluate.
1183 void DecreaseVehicleValue(Vehicle *v)
1185 v->value -= v->value >> 8;
1186 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1189 static const byte _breakdown_chance[64] = {
1190 3, 3, 3, 3, 3, 3, 3, 3,
1191 4, 4, 5, 5, 6, 6, 7, 7,
1192 8, 8, 9, 9, 10, 10, 11, 11,
1193 12, 13, 13, 13, 13, 14, 15, 16,
1194 17, 19, 21, 25, 28, 31, 34, 37,
1195 40, 44, 48, 52, 56, 60, 64, 68,
1196 72, 80, 90, 100, 110, 120, 130, 140,
1197 150, 170, 190, 210, 230, 250, 250, 250,
1200 void CheckVehicleBreakdown(Vehicle *v)
1202 int rel, rel_old;
1204 /* decrease reliability */
1205 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1206 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1208 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1209 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1210 v->cur_speed < 5 || _game_mode == GM_MENU) {
1211 return;
1214 uint32 r = Random();
1216 /* increase chance of failure */
1217 int chance = v->breakdown_chance + 1;
1218 if (Chance16I(1, 25, r)) chance += 25;
1219 v->breakdown_chance = min(255, chance);
1221 /* calculate reliability value to use in comparison */
1222 rel = v->reliability;
1223 if (v->type == VEH_SHIP) rel += 0x6666;
1225 /* reduced breakdowns? */
1226 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1228 /* check if to break down */
1229 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1230 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1231 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1232 v->breakdown_chance = 0;
1237 * Handle all of the aspects of a vehicle breakdown
1238 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1239 * @return true iff the vehicle is stopped because of a breakdown
1240 * @note This function always returns false for aircraft, since these never stop for breakdowns
1242 bool Vehicle::HandleBreakdown()
1244 /* Possible states for Vehicle::breakdown_ctr
1245 * 0 - vehicle is running normally
1246 * 1 - vehicle is currently broken down
1247 * 2 - vehicle is going to break down now
1248 * >2 - vehicle is counting down to the actual breakdown event */
1249 switch (this->breakdown_ctr) {
1250 case 0:
1251 return false;
1253 case 2:
1254 this->breakdown_ctr = 1;
1256 if (this->breakdowns_since_last_service != 255) {
1257 this->breakdowns_since_last_service++;
1260 if (this->type == VEH_AIRCRAFT) {
1261 /* Aircraft just need this flag, the rest is handled elsewhere */
1262 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1263 } else {
1264 this->cur_speed = 0;
1266 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1267 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1268 (this->type == VEH_TRAIN ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1269 (this->type == VEH_TRAIN ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1272 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1273 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1274 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1278 this->MarkDirty(); // Update graphics after speed is zeroed
1279 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1280 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1282 /* FALL THROUGH */
1283 case 1:
1284 /* Aircraft breakdowns end only when arriving at the airport */
1285 if (this->type == VEH_AIRCRAFT) return false;
1287 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1288 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1289 if (--this->breakdown_delay == 0) {
1290 this->breakdown_ctr = 0;
1291 this->MarkDirty();
1292 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1295 return true;
1297 default:
1298 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1299 return false;
1304 * Update age of a vehicle.
1305 * @param v Vehicle to update.
1307 void AgeVehicle(Vehicle *v)
1309 if (v->age < MAX_DAY) {
1310 v->age++;
1311 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1314 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1316 int age = v->age - v->max_age;
1317 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1318 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1319 v->reliability_spd_dec <<= 1;
1322 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1324 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1325 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1327 /* Don't warn if a renew is active */
1328 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1330 StringID str;
1331 if (age == -DAYS_IN_LEAP_YEAR) {
1332 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1333 } else if (age == 0) {
1334 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1335 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1336 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1337 } else {
1338 return;
1341 SetDParam(0, v->index);
1342 AddVehicleAdviceNewsItem(str, v->index);
1346 * Calculates how full a vehicle is.
1347 * @param front The front vehicle of the consist to check.
1348 * @param colour The string to show depending on if we are unloading or loading
1349 * @return A percentage of how full the Vehicle is.
1351 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1353 int count = 0;
1354 int max = 0;
1355 int cars = 0;
1356 int unloading = 0;
1357 bool loading = false;
1359 bool is_loading = front->current_order.IsType(OT_LOADING);
1361 /* The station may be NULL when the (colour) string does not need to be set. */
1362 const Station *st = Station::GetIfValid(front->last_station_visited);
1363 assert(colour == NULL || (st != NULL && is_loading));
1365 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1366 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1368 /* Count up max and used */
1369 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1370 count += v->cargo.StoredCount();
1371 max += v->cargo_cap;
1372 if (v->cargo_cap != 0 && colour != NULL) {
1373 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1374 loading |= !order_no_load &&
1375 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1376 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1377 cars++;
1381 if (colour != NULL) {
1382 if (unloading == 0 && loading) {
1383 *colour = STR_PERCENT_UP;
1384 } else if (unloading == 0 && !loading) {
1385 *colour = STR_PERCENT_NONE;
1386 } else if (cars == unloading || !loading) {
1387 *colour = STR_PERCENT_DOWN;
1388 } else {
1389 *colour = STR_PERCENT_UP_DOWN;
1393 /* Train without capacity */
1394 if (max == 0) return 100;
1396 /* Return the percentage */
1397 return (count * 100) / max;
1401 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1402 * @param v Vehicle that entered a depot.
1404 void VehicleEnterDepot(Vehicle *v)
1406 /* Always work with the front of the vehicle */
1407 assert(v == v->First());
1409 switch (v->type) {
1410 case VEH_TRAIN: {
1411 Train *t = Train::From(v);
1412 SetWindowClassesDirty(WC_TRAINS_LIST);
1413 /* Clear path reservation */
1414 SetDepotReservation(t->tile, false);
1415 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1417 assert(IsSignalBufferEmpty());
1418 AddDepotToSignalBuffer(t->tile, t->owner);
1419 UpdateSignalsInBuffer();
1420 t->wait_counter = 0;
1421 t->force_proceed = TFP_NONE;
1422 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1423 t->ConsistChanged(true);
1424 break;
1427 case VEH_ROAD:
1428 SetWindowClassesDirty(WC_ROADVEH_LIST);
1429 break;
1431 case VEH_SHIP: {
1432 SetWindowClassesDirty(WC_SHIPS_LIST);
1433 Ship *ship = Ship::From(v);
1434 ship->trackdir = TRACKDIR_DEPOT;
1435 ship->UpdateCache();
1436 ship->UpdateViewport(true, true);
1437 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1438 break;
1441 case VEH_AIRCRAFT:
1442 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1443 HandleAircraftEnterHangar(Aircraft::From(v));
1444 break;
1445 default: NOT_REACHED();
1447 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1449 if (v->type != VEH_TRAIN) {
1450 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1451 * 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 */
1452 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1454 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1456 v->vehstatus |= VS_HIDDEN;
1457 v->cur_speed = 0;
1459 VehicleServiceInDepot(v);
1461 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1462 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1463 v->MarkDirty();
1465 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1466 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1468 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1469 Order t = v->current_order;
1470 v->current_order.MakeDummy();
1472 /* Test whether we are heading for this depot. If not, do nothing.
1473 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1474 if ((t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1475 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1476 (v->type == VEH_AIRCRAFT ? t.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1477 /* We are heading for another depot, keep driving. */
1478 return;
1481 if (t.IsRefit()) {
1482 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1483 CommandCost cost = DoCommand(v->tile, v->index, t.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1484 cur_company.Restore();
1486 if (cost.Failed()) {
1487 _vehicles_to_autoreplace[v] = false;
1488 if (v->owner == _local_company) {
1489 /* Notify the user that we stopped the vehicle */
1490 SetDParam(0, v->index);
1491 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1493 } else if (cost.GetCost() != 0) {
1494 v->profit_this_year -= cost.GetCost() << 8;
1495 if (v->owner == _local_company) {
1496 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1501 if (t.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1502 /* Part of orders */
1503 v->DeleteUnreachedImplicitOrders();
1504 UpdateVehicleTimetable(v, true);
1505 v->IncrementImplicitOrderIndex();
1507 if (t.GetDepotActionType() & ODATFB_HALT) {
1508 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1509 _vehicles_to_autoreplace[v] = false;
1510 /* Invalidate last_loading_station. As the link from the station
1511 * before the stop to the station after the stop can't be predicted
1512 * we shouldn't construct it when the vehicle visits the next stop. */
1513 v->last_loading_station = INVALID_STATION;
1514 if (v->owner == _local_company) {
1515 SetDParam(0, v->index);
1516 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1518 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1525 * Update the position of the vehicle. This will update the hash that tells
1526 * which vehicles are on a tile.
1527 * @param v The vehicle to update.
1529 void VehicleUpdatePosition(Vehicle *v)
1531 UpdateVehicleTileHash(v, false);
1535 * Update the vehicle on the viewport, updating the right hash and setting the
1536 * new coordinates.
1537 * @param v The vehicle to update.
1538 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1540 void VehicleUpdateViewport(Vehicle *v, bool dirty)
1542 int img = v->cur_image;
1543 Point pt = RemapCoords(v->x_pos + v->x_offs, v->y_pos + v->y_offs, v->z_pos);
1544 const Sprite *spr = GetSprite(img, ST_NORMAL);
1546 pt.x += spr->x_offs;
1547 pt.y += spr->y_offs;
1549 UpdateVehicleViewportHash(v, pt.x, pt.y);
1551 Rect old_coord = v->coord;
1552 v->coord.left = pt.x;
1553 v->coord.top = pt.y;
1554 v->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
1555 v->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
1557 if (dirty) {
1558 if (old_coord.left == INVALID_COORD) {
1559 MarkSingleVehicleDirty(v);
1560 } else {
1561 MarkAllViewportsDirty(
1562 min(old_coord.left, v->coord.left),
1563 min(old_coord.top, v->coord.top),
1564 max(old_coord.right, v->coord.right) + 1 * ZOOM_LVL_BASE,
1565 max(old_coord.bottom, v->coord.bottom) + 1 * ZOOM_LVL_BASE
1572 * Update the position of the vehicle, and update the viewport.
1573 * @param v The vehicle to update.
1575 void VehicleUpdatePositionAndViewport(Vehicle *v)
1577 VehicleUpdatePosition(v);
1578 VehicleUpdateViewport(v, true);
1582 * Marks viewports dirty where the vehicle's image is.
1583 * @param v vehicle to mark dirty
1585 void MarkSingleVehicleDirty(const Vehicle *v)
1587 MarkAllViewportsDirty(v->coord.left, v->coord.top, v->coord.right + 1 * ZOOM_LVL_BASE, v->coord.bottom + 1 * ZOOM_LVL_BASE);
1591 * Get position information of a vehicle when moving one pixel in the direction it is facing
1592 * @param v Vehicle to move
1593 * @return Position information after the move
1595 VehiclePos GetNewVehiclePos(const Vehicle *v)
1597 static const int8 delta_coord[DIR_END][2] = { /* {x,y} */
1598 {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}
1601 VehiclePos gp;
1602 gp.x = v->x_pos + delta_coord[v->direction][0];
1603 gp.y = v->y_pos + delta_coord[v->direction][1];
1604 gp.new_tile = TileVirtXY(gp.x, gp.y);
1605 return gp;
1608 static const Direction _new_direction_table[] = {
1609 DIR_N, DIR_NW, DIR_W,
1610 DIR_NE, DIR_SE, DIR_SW,
1611 DIR_E, DIR_SE, DIR_S
1614 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1616 int i = 0;
1618 if (y >= v->y_pos) {
1619 if (y != v->y_pos) i += 3;
1620 i += 3;
1623 if (x >= v->x_pos) {
1624 if (x != v->x_pos) i++;
1625 i++;
1628 Direction dir = v->direction;
1630 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1631 if (dirdiff == DIRDIFF_SAME) return dir;
1632 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1636 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1637 * struct initialization, except after each call to this->NextID() the returned value
1638 * is assigned to a vehicle.
1639 * @param type type of vehicle
1640 * @param owner owner of vehicles
1642 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1644 /* Find maximum */
1645 const Vehicle *v;
1646 FOR_ALL_VEHICLES(v) {
1647 if (v->type == type && v->owner == owner) {
1648 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1652 if (this->maxid == 0) return;
1654 /* Reserving 'maxid + 2' because we need:
1655 * - space for the last item (with v->unitnumber == maxid)
1656 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1657 this->cache = CallocT<bool>(this->maxid + 2);
1659 /* Fill the cache */
1660 FOR_ALL_VEHICLES(v) {
1661 if (v->type == type && v->owner == owner) {
1662 this->cache[v->unitnumber] = true;
1667 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1668 UnitID FreeUnitIDGenerator::NextID()
1670 if (this->maxid <= this->curid) return ++this->curid;
1672 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1674 return this->curid;
1678 * Get an unused unit number for a vehicle (if allowed).
1679 * @param type Type of vehicle
1680 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1682 UnitID GetFreeUnitNumber(VehicleType type)
1684 /* Check whether it is allowed to build another vehicle. */
1685 uint max_veh;
1686 switch (type) {
1687 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1688 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1689 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1690 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1691 default: NOT_REACHED();
1694 const Company *c = Company::Get(_current_company);
1695 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1697 FreeUnitIDGenerator gen(type, _current_company);
1699 return gen.NextID();
1704 * Check whether we can build infrastructure for the given
1705 * vehicle type. This to disable building stations etc. when
1706 * you are not allowed/able to have the vehicle type yet.
1707 * @param type the vehicle type to check this for
1708 * @return true if there is any reason why you may build
1709 * the infrastructure for the given vehicle type
1711 bool CanBuildVehicleInfrastructure(VehicleType type)
1713 assert(IsCompanyBuildableVehicleType(type));
1715 if (!Company::IsValidID(_local_company)) return false;
1716 if (!_settings_client.gui.disable_unsuitable_building) return true;
1718 UnitID max;
1719 switch (type) {
1720 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1721 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1722 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1723 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1724 default: NOT_REACHED();
1727 /* We can build vehicle infrastructure when we may build the vehicle type */
1728 if (max > 0) {
1729 /* Can we actually build the vehicle type? */
1730 const Engine *e;
1731 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1732 if (HasBit(e->company_avail, _local_company)) return true;
1734 return false;
1737 /* We should be able to build infrastructure when we have the actual vehicle type */
1738 const Vehicle *v;
1739 FOR_ALL_VEHICLES(v) {
1740 if (v->owner == _local_company && v->type == type) return true;
1743 return false;
1748 * Determines the #LiveryScheme for a vehicle.
1749 * @param engine_type Engine of the vehicle.
1750 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1751 * @param v the vehicle, \c NULL if in purchase list etc.
1752 * @return livery scheme to use.
1754 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1756 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1757 const Engine *e = Engine::Get(engine_type);
1758 switch (e->type) {
1759 default: NOT_REACHED();
1760 case VEH_TRAIN:
1761 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1762 /* Wagonoverrides use the colour scheme of the front engine.
1763 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1764 engine_type = parent_engine_type;
1765 e = Engine::Get(engine_type);
1766 /* Note: Luckily cargo_type is not needed for engines */
1769 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1770 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1771 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1772 if (!CargoSpec::Get(cargo_type)->is_freight) {
1773 if (parent_engine_type == INVALID_ENGINE) {
1774 return LS_PASSENGER_WAGON_STEAM;
1775 } else {
1776 switch (RailVehInfo(parent_engine_type)->engclass) {
1777 default: NOT_REACHED();
1778 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1779 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1780 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1781 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1782 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1785 } else {
1786 return LS_FREIGHT_WAGON;
1788 } else {
1789 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1791 switch (e->u.rail.engclass) {
1792 default: NOT_REACHED();
1793 case EC_STEAM: return LS_STEAM;
1794 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1795 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1796 case EC_MONORAIL: return LS_MONORAIL;
1797 case EC_MAGLEV: return LS_MAGLEV;
1801 case VEH_ROAD:
1802 /* Always use the livery of the front */
1803 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1804 engine_type = parent_engine_type;
1805 e = Engine::Get(engine_type);
1806 cargo_type = v->First()->cargo_type;
1808 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1809 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1811 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1812 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1813 /* Tram */
1814 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1815 } else {
1816 /* Bus or truck */
1817 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1820 case VEH_SHIP:
1821 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1822 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1823 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1825 case VEH_AIRCRAFT:
1826 switch (e->u.air.subtype) {
1827 case AIR_HELI: return LS_HELICOPTER;
1828 case AIR_CTOL: return LS_SMALL_PLANE;
1829 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1830 default: NOT_REACHED();
1836 * Determines the livery for a vehicle.
1837 * @param engine_type EngineID of the vehicle
1838 * @param company Owner of the vehicle
1839 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1840 * @param v the vehicle. NULL if in purchase list etc.
1841 * @param livery_setting The livery settings to use for acquiring the livery information.
1842 * @return livery to use
1844 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1846 const Company *c = Company::Get(company);
1847 LiveryScheme scheme = LS_DEFAULT;
1849 /* The default livery is always available for use, but its in_use flag determines
1850 * whether any _other_ liveries are in use. */
1851 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1852 /* Determine the livery scheme to use */
1853 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1855 /* Switch back to the default scheme if the resolved scheme is not in use */
1856 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1859 return &c->livery[scheme];
1863 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1865 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1867 /* Return cached value if any */
1868 if (map != PAL_NONE) return map;
1870 const Engine *e = Engine::Get(engine_type);
1872 /* Check if we should use the colour map callback */
1873 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1874 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1875 /* Failure means "use the default two-colour" */
1876 if (callback != CALLBACK_FAILED) {
1877 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1878 map = GB(callback, 0, 14);
1879 /* If bit 14 is set, then the company colours are applied to the
1880 * map else it's returned as-is. */
1881 if (!HasBit(callback, 14)) {
1882 /* Update cache */
1883 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1884 return map;
1889 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1891 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1893 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1894 if (!Company::IsValidID(company)) return map;
1896 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1898 map += livery->colour1;
1899 if (twocc) map += livery->colour2 * 16;
1901 /* Update cache */
1902 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1903 return map;
1907 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
1908 * @param engine_type ID of engine
1909 * @param company ID of company
1910 * @return A ready-to-use palette modifier
1912 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1914 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1918 * Get the colour map for a vehicle.
1919 * @param v Vehicle to get colour map for
1920 * @return A ready-to-use palette modifier
1922 PaletteID GetVehiclePalette(const Vehicle *v)
1924 if (v->IsGroundVehicle()) {
1925 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1928 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1932 * Delete all implicit orders which were not reached.
1934 void Vehicle::DeleteUnreachedImplicitOrders()
1936 if (this->IsGroundVehicle()) {
1937 uint16 &gv_flags = this->GetGroundVehicleFlags();
1938 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1939 /* Do not delete orders, only skip them */
1940 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1941 this->cur_implicit_order_index = this->cur_real_order_index;
1942 InvalidateVehicleOrder(this, 0);
1943 return;
1947 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1948 while (order != NULL) {
1949 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1951 if (order->IsType(OT_IMPLICIT)) {
1952 DeleteOrder(this, this->cur_implicit_order_index);
1953 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1954 order = this->GetOrder(this->cur_implicit_order_index);
1955 } else {
1956 /* Skip non-implicit orders, e.g. service-orders */
1957 order = order->next;
1958 this->cur_implicit_order_index++;
1961 /* Wrap around */
1962 if (order == NULL) {
1963 order = this->GetOrder(0);
1964 this->cur_implicit_order_index = 0;
1970 * Prepare everything to begin the loading when arriving at a station.
1971 * @pre IsStationTile(this->tile) || this->type == VEH_SHIP.
1973 void Vehicle::BeginLoading()
1975 assert(IsStationTile(this->tile) || this->type == VEH_SHIP);
1977 if (this->current_order.IsType(OT_GOTO_STATION) &&
1978 this->current_order.GetDestination() == this->last_station_visited) {
1979 this->DeleteUnreachedImplicitOrders();
1981 /* Now both order indices point to the destination station, and we can start loading */
1982 this->current_order.MakeLoading(true);
1983 UpdateVehicleTimetable(this, true);
1985 /* Furthermore add the Non Stop flag to mark that this station
1986 * is the actual destination of the vehicle, which is (for example)
1987 * necessary to be known for HandleTrainLoading to determine
1988 * whether the train is lost or not; not marking a train lost
1989 * that arrives at random stations is bad. */
1990 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1992 } else {
1993 /* We weren't scheduled to stop here. Insert an implicit order
1994 * to show that we are stopping here.
1995 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
1996 * the 'wrong' terminal when skipping orders etc. */
1997 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
1998 if (this->IsGroundVehicle() &&
1999 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2000 in_list->GetDestination() != this->last_station_visited)) {
2001 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2002 /* Do not create consecutive duplicates of implicit orders */
2003 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2004 if (prev_order == NULL ||
2005 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2006 prev_order->GetDestination() != this->last_station_visited) {
2008 /* Prefer deleting implicit orders instead of inserting new ones,
2009 * so test whether the right order follows later. In case of only
2010 * implicit orders treat the last order in the list like an
2011 * explicit one, except if the overall number of orders surpasses
2012 * IMPLICIT_ORDER_ONLY_CAP. */
2013 int target_index = this->cur_implicit_order_index;
2014 bool found = false;
2015 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2016 const Order *order = this->GetOrder(target_index);
2017 if (order == NULL) break; // No orders.
2018 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2019 found = true;
2020 break;
2022 target_index++;
2023 if (target_index >= this->orders.list->GetNumOrders()) {
2024 if (this->GetNumManualOrders() == 0 &&
2025 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2026 break;
2028 target_index = 0;
2030 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2033 if (found) {
2034 if (suppress_implicit_orders) {
2035 /* Skip to the found order */
2036 this->cur_implicit_order_index = target_index;
2037 InvalidateVehicleOrder(this, 0);
2038 } else {
2039 /* Delete all implicit orders up to the station we just reached */
2040 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2041 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2042 if (order->IsType(OT_IMPLICIT)) {
2043 DeleteOrder(this, this->cur_implicit_order_index);
2044 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2045 order = this->GetOrder(this->cur_implicit_order_index);
2046 } else {
2047 /* Skip non-implicit orders, e.g. service-orders */
2048 order = order->next;
2049 this->cur_implicit_order_index++;
2052 /* Wrap around */
2053 if (order == NULL) {
2054 order = this->GetOrder(0);
2055 this->cur_implicit_order_index = 0;
2057 assert(order != NULL);
2060 } else if (!suppress_implicit_orders &&
2061 ((this->orders.list == NULL && OrderList::CanAllocateItem()) ||
2062 this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID) &&
2063 Order::CanAllocateItem()) {
2064 /* Insert new implicit order */
2065 Order *implicit_order = new Order();
2066 implicit_order->MakeImplicit(this->last_station_visited);
2067 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2068 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2070 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2071 * Reenable it for this vehicle */
2072 uint16 &gv_flags = this->GetGroundVehicleFlags();
2073 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2077 this->current_order.MakeLoading(false);
2080 if (this->last_loading_station != INVALID_STATION &&
2081 this->last_loading_station != this->last_station_visited &&
2082 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2083 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2084 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2087 PrepareUnload(this);
2089 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2090 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2091 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2092 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2094 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2095 this->cur_speed = 0;
2096 this->MarkDirty();
2100 * Return all reserved cargo packets to the station and reset all packets
2101 * staged for transfer.
2102 * @param st the station where the reserved packets should go.
2104 void Vehicle::CancelReservation(StationID next, Station *st)
2106 for (Vehicle *v = this; v != NULL; v = v->next) {
2107 VehicleCargoList &cargo = v->cargo;
2108 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2109 DEBUG(misc, 1, "cancelling cargo reservation");
2110 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2111 cargo.SetTransferLoadPlace(st->xy);
2113 cargo.KeepAll();
2118 * Perform all actions when leaving a station.
2119 * @pre this->current_order.IsType(OT_LOADING)
2121 void Vehicle::LeaveStation()
2123 assert(this->current_order.IsType(OT_LOADING));
2125 delete this->cargo_payment;
2127 /* Only update the timetable if the vehicle was supposed to stop here. */
2128 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2130 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2131 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2132 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2133 /* Refresh next hop stats to make sure we've done that at least once
2134 * during the stop and that refit_cap == cargo_cap for each vehicle in
2135 * the consist. */
2136 this->ResetRefitCaps();
2137 LinkRefresher::Run(this);
2139 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2140 this->last_loading_station = this->last_station_visited;
2141 } else {
2142 /* if the vehicle couldn't load and had to unload or transfer everything
2143 * set the last loading station to invalid as it will leave empty. */
2144 this->last_loading_station = INVALID_STATION;
2148 this->current_order.MakeLeaveStation();
2149 Station *st = Station::Get(this->last_station_visited);
2150 this->CancelReservation(INVALID_STATION, st);
2151 st->loading_vehicles.remove(this);
2153 HideFillingPercent(&this->fill_percent_te_id);
2155 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2156 /* Trigger station animation (trains only) */
2157 if (IsStationTile(this->tile)) {
2158 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2159 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2162 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2167 * Reset all refit_cap in the consist to cargo_cap.
2169 void Vehicle::ResetRefitCaps()
2171 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2175 * Handle the loading of the vehicle; when not it skips through dummy
2176 * orders and does nothing in all other cases.
2177 * @param mode is the non-first call for this vehicle in this tick?
2179 void Vehicle::HandleLoading(bool mode)
2181 switch (this->current_order.GetType()) {
2182 case OT_LOADING: {
2183 uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
2185 /* Not the first call for this tick, or still loading */
2186 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2188 this->PlayLeaveStationSound();
2190 this->LeaveStation();
2192 /* Only advance to next order if we just loaded at the current one */
2193 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2194 if (order == NULL ||
2195 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2196 order->GetDestination() != this->last_station_visited) {
2197 return;
2199 break;
2202 case OT_DUMMY: break;
2204 default: return;
2207 this->IncrementImplicitOrderIndex();
2211 * Get a map of cargoes and free capacities in the consist.
2212 * @param capacities Map to be filled with cargoes and capacities.
2214 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2216 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2217 if (v->cargo_cap == 0) continue;
2218 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2219 if (pair == capacities.End()) {
2220 pair = capacities.Append();
2221 pair->first = v->cargo_type;
2222 pair->second = v->cargo_cap - v->cargo.StoredCount();
2223 } else {
2224 pair->second += v->cargo_cap - v->cargo.StoredCount();
2229 uint Vehicle::GetConsistTotalCapacity() const
2231 uint result = 0;
2232 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2233 result += v->cargo_cap;
2235 return result;
2239 * Send this vehicle to the depot using the given command(s).
2240 * @param flags the command flags (like execute and such).
2241 * @param command the command to execute.
2242 * @return the cost of the depot action.
2244 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2246 CommandCost ret = CheckOwnership(this->owner);
2247 if (ret.Failed()) return ret;
2249 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2250 if (this->IsStoppedInDepot()) return CMD_ERROR;
2252 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2253 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2254 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2255 /* We called with a different DEPOT_SERVICE setting.
2256 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2257 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2258 if (flags & DC_EXEC) {
2259 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2260 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2261 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2263 return CommandCost();
2266 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2267 if (flags & DC_EXEC) {
2268 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2269 * then skip to the next order; effectively cancelling this forced service */
2270 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2272 if (this->IsGroundVehicle()) {
2273 uint16 &gv_flags = this->GetGroundVehicleFlags();
2274 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2277 this->current_order.MakeDummy();
2278 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2280 return CommandCost();
2283 TileIndex location;
2284 DestinationID destination;
2285 bool reverse;
2286 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};
2287 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2289 if (flags & DC_EXEC) {
2290 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2292 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2293 uint16 &gv_flags = this->GetGroundVehicleFlags();
2294 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2297 this->dest_tile = location;
2298 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2299 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2300 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2302 /* If there is no depot in front, reverse automatically (trains only) */
2303 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2305 if (this->type == VEH_AIRCRAFT) {
2306 Aircraft *a = Aircraft::From(this);
2307 if (a->state == FLYING && a->targetairport != destination) {
2308 /* The aircraft is now heading for a different hangar than the next in the orders */
2309 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2310 AircraftNextAirportPos_and_Order(a);
2315 return CommandCost();
2320 * Update the cached visual effect.
2321 * @param allow_power_change true if the wagon-is-powered-state may change.
2323 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2325 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2326 const Engine *e = this->GetEngine();
2328 /* Evaluate properties */
2329 byte visual_effect;
2330 switch (e->type) {
2331 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2332 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2333 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2334 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2337 /* Check powered wagon / visual effect callback */
2338 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2339 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2341 if (callback != CALLBACK_FAILED) {
2342 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2344 callback = GB(callback, 0, 8);
2345 /* Avoid accidentally setting 'visual_effect' to the default value
2346 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2347 if (callback == VE_DEFAULT) {
2348 assert(HasBit(callback, VE_DISABLE_EFFECT));
2349 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2351 visual_effect = callback;
2355 /* Apply default values */
2356 if (visual_effect == VE_DEFAULT ||
2357 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2358 /* Only train engines have default effects.
2359 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2360 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2361 if (visual_effect == VE_DEFAULT) {
2362 visual_effect = 1 << VE_DISABLE_EFFECT;
2363 } else {
2364 SetBit(visual_effect, VE_DISABLE_EFFECT);
2366 } else {
2367 if (visual_effect == VE_DEFAULT) {
2368 /* Also set the offset */
2369 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2371 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2375 this->vcache.cached_vis_effect = visual_effect;
2377 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2378 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2379 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2383 static const int8 _vehicle_smoke_pos[8] = {
2384 1, 1, 1, 0, -1, -1, -1, 0
2388 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2389 * @pre this->IsPrimaryVehicle()
2391 void Vehicle::ShowVisualEffect() const
2393 assert(this->IsPrimaryVehicle());
2394 bool sound = false;
2396 /* Do not show any smoke when:
2397 * - vehicle smoke is disabled by the player
2398 * - the vehicle is slowing down or stopped (by the player)
2399 * - the vehicle is moving very slowly
2401 if (_settings_game.vehicle.smoke_amount == 0 ||
2402 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2403 this->cur_speed < 2) {
2404 return;
2407 uint max_speed = this->vcache.cached_max_speed;
2408 if (this->type == VEH_TRAIN) {
2409 const Train *t = Train::From(this);
2410 /* For trains, do not show any smoke when:
2411 * - the train is reversing
2412 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2414 if (HasBit(t->flags, VRF_REVERSING) ||
2415 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2416 t->cur_speed >= t->Train::GetCurrentMaxSpeed())) {
2417 return;
2420 max_speed = min(max_speed, t->gcache.cached_max_track_speed);
2421 max_speed = min(max_speed, this->current_order.max_speed);
2423 if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2);
2425 const Vehicle *v = this;
2427 do {
2428 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2429 byte effect_type = GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2430 bool disable_effect = HasBit(v->vcache.cached_vis_effect, VE_DISABLE_EFFECT);
2432 /* Show no smoke when:
2433 * - Smoke has been disabled for this vehicle
2434 * - The vehicle is not visible
2435 * - The vehicle is under a bridge
2436 * - The vehicle is on a depot tile
2437 * - The vehicle is on a tunnel tile
2438 * - The vehicle is a train engine that is currently unpowered */
2439 if (disable_effect ||
2440 v->vehstatus & VS_HIDDEN ||
2441 HasBridgeAbove(v->tile) ||
2442 IsDepotTile(v->tile) ||
2443 IsTunnelTile(v->tile) ||
2444 (v->type == VEH_TRAIN &&
2445 !HasPowerOnRail(Train::From(v)->railtype, Train::From(v)->GetTrackRailType()))) {
2446 continue;
2449 /* The effect offset is relative to a point 4 units behind the vehicle's
2450 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2451 * correction factor. */
2452 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2454 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2455 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2457 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2458 x = -x;
2459 y = -y;
2462 switch (effect_type) {
2463 case VE_TYPE_STEAM:
2464 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2465 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2466 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2467 * REGULATION:
2468 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2469 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2470 CreateEffectVehicleRel(v, x, y, 10, EV_STEAM_SMOKE);
2471 sound = true;
2473 break;
2475 case VE_TYPE_DIESEL: {
2476 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2477 * when smoke emission stops.
2478 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2479 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2480 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2481 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2482 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2483 * maximum speed no diesel_smoke is emitted.
2484 * REGULATION:
2485 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2486 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2487 int power_weight_effect = 0;
2488 if (v->type == VEH_TRAIN) {
2489 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2491 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2492 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2493 CreateEffectVehicleRel(v, x, y, 10, EV_DIESEL_SMOKE);
2494 sound = true;
2496 break;
2499 case VE_TYPE_ELECTRIC:
2500 /* Electric train's spark - more often occurs when train is departing (more load)
2501 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2502 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2503 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2504 * REGULATION:
2505 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2506 if (GB(v->tick_counter, 0, 2) == 0 &&
2507 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2508 CreateEffectVehicleRel(v, x, y, 10, EV_ELECTRIC_SPARK);
2509 sound = true;
2511 break;
2513 default:
2514 break;
2516 } while ((v = v->Next()) != NULL);
2518 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2522 * Set the next vehicle of this vehicle.
2523 * @param next the next vehicle. NULL removes the next vehicle.
2525 void Vehicle::SetNext(Vehicle *next)
2527 assert(this != next);
2529 if (this->next != NULL) {
2530 /* We had an old next vehicle. Update the first and previous pointers */
2531 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2532 v->first = this->next;
2534 this->next->previous = NULL;
2537 this->next = next;
2539 if (this->next != NULL) {
2540 /* A new next vehicle. Update the first and previous pointers */
2541 if (this->next->previous != NULL) this->next->previous->next = NULL;
2542 this->next->previous = this;
2543 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2544 v->first = this->first;
2550 * Adds this vehicle to a shared vehicle chain.
2551 * @param shared_chain a vehicle of the chain with shared vehicles.
2552 * @pre !this->IsOrderListShared()
2554 void Vehicle::AddToShared(Vehicle *shared_chain)
2556 assert(this->previous_shared == NULL && this->next_shared == NULL);
2558 if (shared_chain->orders.list == NULL) {
2559 assert(shared_chain->previous_shared == NULL);
2560 assert(shared_chain->next_shared == NULL);
2561 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2564 this->next_shared = shared_chain->next_shared;
2565 this->previous_shared = shared_chain;
2567 shared_chain->next_shared = this;
2569 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2571 shared_chain->orders.list->AddVehicle(this);
2575 * Removes the vehicle from the shared order list.
2577 void Vehicle::RemoveFromShared()
2579 /* Remember if we were first and the old window number before RemoveVehicle()
2580 * as this changes first if needed. */
2581 bool were_first = (this->FirstShared() == this);
2582 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2584 this->orders.list->RemoveVehicle(this);
2586 if (!were_first) {
2587 /* We are not the first shared one, so only relink our previous one. */
2588 this->previous_shared->next_shared = this->NextShared();
2591 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2594 if (this->orders.list->GetNumVehicles() == 1) {
2595 /* When there is only one vehicle, remove the shared order list window. */
2596 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2597 InvalidateVehicleOrder(this->FirstShared(), 0);
2598 } else if (were_first) {
2599 /* If we were the first one, update to the new first one.
2600 * Note: FirstShared() is already the new first */
2601 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2604 this->next_shared = NULL;
2605 this->previous_shared = NULL;
2608 void VehiclesYearlyLoop()
2610 Vehicle *v;
2611 FOR_ALL_VEHICLES(v) {
2612 if (v->IsPrimaryVehicle()) {
2613 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2614 Money profit = v->GetDisplayProfitThisYear();
2615 if (v->age >= 730 && profit < 0) {
2616 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2617 SetDParam(0, v->index);
2618 SetDParam(1, profit);
2619 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2621 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2624 v->profit_last_year = v->profit_this_year;
2625 v->profit_this_year = 0;
2626 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2629 GroupStatistics::UpdateProfits();
2630 SetWindowClassesDirty(WC_TRAINS_LIST);
2631 SetWindowClassesDirty(WC_SHIPS_LIST);
2632 SetWindowClassesDirty(WC_ROADVEH_LIST);
2633 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2638 * Can this station be used by the given engine type?
2639 * @param engine_type the type of vehicles to test
2640 * @param st the station to test for
2641 * @return true if and only if the vehicle of the type can use this station.
2642 * @note For road vehicles the Vehicle is needed to determine whether it can
2643 * use the station. This function will return true for road vehicles
2644 * when at least one of the facilities is available.
2646 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2648 const Engine *e = Engine::GetIfValid(engine_type);
2649 assert(e != NULL);
2651 switch (e->type) {
2652 case VEH_TRAIN:
2653 return (st->facilities & FACIL_TRAIN) != 0;
2655 case VEH_ROAD:
2656 /* For road vehicles we need the vehicle to know whether it can actually
2657 * use the station, but if it doesn't have facilities for RVs it is
2658 * certainly not possible that the station can be used. */
2659 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2661 case VEH_SHIP:
2662 return (st->facilities & FACIL_DOCK) != 0;
2664 case VEH_AIRCRAFT:
2665 return (st->facilities & FACIL_AIRPORT) != 0 &&
2666 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2668 default:
2669 return false;
2674 * Can this station be used by the given vehicle?
2675 * @param v the vehicle to test
2676 * @param st the station to test for
2677 * @return true if and only if the vehicle can use this station.
2679 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2681 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2683 return CanVehicleUseStation(v->engine_type, st);
2687 * Access the ground vehicle cache of the vehicle.
2688 * @pre The vehicle is a #GroundVehicle.
2689 * @return #GroundVehicleCache of the vehicle.
2691 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2693 assert(this->IsGroundVehicle());
2694 if (this->type == VEH_TRAIN) {
2695 return &Train::From(this)->gcache;
2696 } else {
2697 return &RoadVehicle::From(this)->gcache;
2702 * Access the ground vehicle cache of the vehicle.
2703 * @pre The vehicle is a #GroundVehicle.
2704 * @return #GroundVehicleCache of the vehicle.
2706 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2708 assert(this->IsGroundVehicle());
2709 if (this->type == VEH_TRAIN) {
2710 return &Train::From(this)->gcache;
2711 } else {
2712 return &RoadVehicle::From(this)->gcache;
2717 * Access the ground vehicle flags of the vehicle.
2718 * @pre The vehicle is a #GroundVehicle.
2719 * @return #GroundVehicleFlags of the vehicle.
2721 uint16 &Vehicle::GetGroundVehicleFlags()
2723 assert(this->IsGroundVehicle());
2724 if (this->type == VEH_TRAIN) {
2725 return Train::From(this)->gv_flags;
2726 } else {
2727 return RoadVehicle::From(this)->gv_flags;
2732 * Access the ground vehicle flags of the vehicle.
2733 * @pre The vehicle is a #GroundVehicle.
2734 * @return #GroundVehicleFlags of the vehicle.
2736 const uint16 &Vehicle::GetGroundVehicleFlags() const
2738 assert(this->IsGroundVehicle());
2739 if (this->type == VEH_TRAIN) {
2740 return Train::From(this)->gv_flags;
2741 } else {
2742 return RoadVehicle::From(this)->gv_flags;
2747 * Calculates the set of vehicles that will be affected by a given selection.
2748 * @param set [inout] Set of affected vehicles.
2749 * @param v First vehicle of the selection.
2750 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2751 * @pre \a set must be empty.
2752 * @post \a set will contain the vehicles that will be refitted.
2754 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2756 if (v->type == VEH_TRAIN) {
2757 Train *u = Train::From(v);
2758 /* Only include whole vehicles, so start with the first articulated part */
2759 u = u->GetFirstEnginePart();
2761 /* Include num_vehicles vehicles, not counting articulated parts */
2762 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2763 do {
2764 /* Include current vehicle in the selection. */
2765 set.Include(u->index);
2767 /* If the vehicle is multiheaded, add the other part too. */
2768 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2770 u = u->Next();
2771 } while (u != NULL && u->IsArticulatedPart());