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/>.
10 /** @file vehicle.cpp Base implementations of all vehicles. */
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"
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"
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"
52 #include "signal_func.h"
53 #include "linkgraph/linkgraph.h"
54 #include "linkgraph/refresh.h"
56 #include "table/strings.h"
59 VehicleID _new_vehicle_id
;
60 uint16 _returned_refit_capacity
; ///< Stores the capacity after a refit operation.
61 uint16 _returned_mail_refit_capacity
; ///< Stores the mail capacity after a refit operation (Aircraft only).
64 /** The pool with all our precious vehicles. */
65 VehiclePool
_vehicle_pool("Vehicle");
66 INSTANTIATE_POOL_METHODS(Vehicle
)
69 * Function to tell if a vehicle needs to be autorenewed
70 * @param *c The vehicle owner
71 * @param use_renew_setting Should the company renew setting be considered?
72 * @return true if the vehicle is old enough for replacement
74 bool Vehicle::NeedsAutorenewing(const Company
*c
, bool use_renew_setting
) const
76 /* We can always generate the Company pointer when we have the vehicle.
77 * However this takes time and since the Company pointer is often present
78 * when this function is called then it's faster to pass the pointer as an
79 * argument rather than finding it again. */
80 assert(c
== Company::Get(this->owner
));
82 if (use_renew_setting
&& !c
->settings
.engine_renew
) return false;
83 if (this->age
- this->max_age
< (c
->settings
.engine_renew_months
* 30)) return false;
85 /* Only engines need renewing */
86 if (this->type
== VEH_TRAIN
&& !Train::From(this)->IsEngine()) return false;
92 * Service a vehicle and all subsequent vehicles in the consist
94 * @param *v The vehicle or vehicle chain being serviced
96 void VehicleServiceInDepot(Vehicle
*v
)
99 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
); // ensure that last service date and reliability are updated
102 v
->date_of_last_service
= _date
;
103 v
->breakdowns_since_last_service
= 0;
104 v
->reliability
= v
->GetEngine()->reliability
;
105 /* Prevent vehicles from breaking down directly after exiting the depot. */
106 v
->breakdown_chance
/= 4;
108 } while (v
!= NULL
&& v
->HasEngineType());
112 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
114 * @see NeedsAutomaticServicing()
115 * @return true if the vehicle should go to a depot if a opportunity presents itself.
117 bool Vehicle::NeedsServicing() const
119 /* Stopped or crashed vehicles will not move, as such making unmovable
120 * vehicles to go for service is lame. */
121 if (this->vehstatus
& (VS_STOPPED
| VS_CRASHED
)) return false;
123 /* Are we ready for the next service cycle? */
124 const Company
*c
= Company::Get(this->owner
);
125 if (this->ServiceIntervalIsPercent() ?
126 (this->reliability
>= this->GetEngine()->reliability
* (100 - this->GetServiceInterval()) / 100) :
127 (this->date_of_last_service
+ this->GetServiceInterval() >= _date
)) {
131 /* If we're servicing anyway, because we have not disabled servicing when
132 * there are no breakdowns or we are playing with breakdowns, bail out. */
133 if (!_settings_game
.order
.no_servicing_if_no_breakdowns
||
134 _settings_game
.difficulty
.vehicle_breakdowns
!= 0) {
138 /* Test whether there is some pending autoreplace.
139 * Note: We do this after the service-interval test.
140 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
141 bool pending_replace
= false;
142 Money needed_money
= c
->settings
.engine_renew_money
;
143 if (needed_money
> c
->money
) return false;
145 for (const Vehicle
*v
= this; v
!= NULL
; v
= (v
->type
== VEH_TRAIN
) ? Train::From(v
)->GetNextUnit() : NULL
) {
146 bool replace_when_old
= false;
147 EngineID new_engine
= EngineReplacementForCompany(c
, v
->engine_type
, v
->group_id
, &replace_when_old
);
149 /* Check engine availability */
150 if (new_engine
== INVALID_ENGINE
|| !HasBit(Engine::Get(new_engine
)->company_avail
, v
->owner
)) continue;
151 /* Is the vehicle old if we are not always replacing? */
152 if (replace_when_old
&& !v
->NeedsAutorenewing(c
, false)) continue;
154 /* Check refittability */
155 uint32 available_cargo_types
, union_mask
;
156 GetArticulatedRefitMasks(new_engine
, true, &union_mask
, &available_cargo_types
);
157 /* Is there anything to refit? */
158 if (union_mask
!= 0) {
160 /* We cannot refit to mixed cargoes in an automated way */
161 if (IsArticulatedVehicleCarryingDifferentCargoes(v
, &cargo_type
)) continue;
163 /* Did the old vehicle carry anything? */
164 if (cargo_type
!= CT_INVALID
) {
165 /* We can't refit the vehicle to carry the cargo we want */
166 if (!HasBit(available_cargo_types
, cargo_type
)) continue;
171 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
172 pending_replace
= true;
173 needed_money
+= 2 * Engine::Get(new_engine
)->GetCost();
174 if (needed_money
> c
->money
) return false;
177 return pending_replace
;
181 * Checks if the current order should be interrupted for a service-in-depot order.
182 * @see NeedsServicing()
183 * @return true if the current order should be interrupted.
185 bool Vehicle::NeedsAutomaticServicing() const
187 if (this->HasDepotOrder()) return false;
188 if (this->current_order
.IsType(OT_LOADING
)) return false;
189 if (this->current_order
.IsType(OT_GOTO_DEPOT
) && this->current_order
.GetDepotOrderType() != ODTFB_SERVICE
) return false;
190 return NeedsServicing();
193 uint
Vehicle::Crash(bool flooded
)
195 assert((this->vehstatus
& VS_CRASHED
) == 0);
196 assert(this->Previous() == NULL
); // IsPrimaryVehicle fails for free-wagon-chains
199 /* Stop the vehicle. */
200 if (this->IsPrimaryVehicle()) this->vehstatus
|= VS_STOPPED
;
201 /* crash all wagons, and count passengers */
202 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) {
203 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
204 if (IsCargoInClass(v
->cargo_type
, CC_PASSENGERS
)) pass
+= v
->cargo
.TotalCount();
205 v
->vehstatus
|= VS_CRASHED
;
206 MarkSingleVehicleDirty(v
);
209 /* Dirty some windows */
210 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
211 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
212 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
213 SetWindowDirty(WC_VEHICLE_DEPOT
, this->tile
);
215 delete this->cargo_payment
;
216 this->cargo_payment
= NULL
;
218 return RandomRange(pass
+ 1); // Randomise deceased passengers.
223 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
224 * @param engine The engine that caused the problem
225 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
226 * @param part2 Part 2 of the error message, taking the engine as parameter 2
227 * @param bug_type Flag to check and set in grfconfig
228 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
230 void ShowNewGrfVehicleError(EngineID engine
, StringID part1
, StringID part2
, GRFBugs bug_type
, bool critical
)
232 const Engine
*e
= Engine::Get(engine
);
233 GRFConfig
*grfconfig
= GetGRFConfig(e
->GetGRFID());
235 if (!HasBit(grfconfig
->grf_bugs
, bug_type
)) {
236 SetBit(grfconfig
->grf_bugs
, bug_type
);
237 SetDParamStr(0, grfconfig
->GetName());
238 SetDParam(1, engine
);
239 ShowErrorMessage(part1
, part2
, WL_CRITICAL
);
240 if (!_networking
) DoCommand(0, critical
? PM_PAUSED_ERROR
: PM_PAUSED_NORMAL
, 1, DC_EXEC
, CMD_PAUSE
);
246 SetDParamStr(0, grfconfig
->GetName());
247 GetString(buffer
, part1
, lastof(buffer
));
248 DEBUG(grf
, 0, "%s", buffer
+ 3);
250 SetDParam(1, engine
);
251 GetString(buffer
, part2
, lastof(buffer
));
252 DEBUG(grf
, 0, "%s", buffer
+ 3);
256 * Logs a bug in GRF and shows a warning message if this
257 * is for the first time this happened.
258 * @param u first vehicle of chain
260 void VehicleLengthChanged(const Vehicle
*u
)
262 /* show a warning once for each engine in whole game and once for each GRF after each game load */
263 const Engine
*engine
= u
->GetEngine();
264 uint32 grfid
= engine
->grf_prop
.grffile
->grfid
;
265 GRFConfig
*grfconfig
= GetGRFConfig(grfid
);
266 if (GamelogGRFBugReverse(grfid
, engine
->grf_prop
.local_id
) || !HasBit(grfconfig
->grf_bugs
, GBUG_VEH_LENGTH
)) {
267 ShowNewGrfVehicleError(u
->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_VEHICLE_LENGTH
, GBUG_VEH_LENGTH
, true);
272 * Vehicle constructor.
273 * @param type Type of the new vehicle.
275 Vehicle::Vehicle(VehicleType type
)
278 this->coord
.left
= INVALID_COORD
;
279 this->group_id
= DEFAULT_GROUP
;
280 this->fill_percent_te_id
= INVALID_TE_ID
;
282 this->colourmap
= PAL_NONE
;
283 this->cargo_age_counter
= 1;
284 this->last_station_visited
= INVALID_STATION
;
285 this->last_loading_station
= INVALID_STATION
;
289 * Get a value for a vehicle's random_bits.
290 * @return A random value from 0 to 255.
292 byte
VehicleRandomBits()
294 return GB(Random(), 0, 8);
299 * Vehicle hash update template function
300 * @param v The vehicle to update
301 * @param link The vehicle link to update within Vehicle
302 * @param old_hash The hash to remove the vehicle from, if any
303 * @param new_hash The hash to add the vehicle to, if any
305 static void UpdateVehicleHash(Vehicle
*v
, VehicleHashLink
Vehicle::*link
, Vehicle
**old_hash
, Vehicle
**new_hash
)
307 if (old_hash
== new_hash
) return;
309 /* Remove from the old position in the hash table */
310 if (old_hash
!= NULL
) {
311 if ((v
->*link
).next
!= NULL
) ((v
->*link
).next
->*link
).pprev
= (v
->*link
).pprev
;
312 *(v
->*link
).pprev
= (v
->*link
).next
;
315 /* Insert vehicle at beginning of the new position in the hash table */
316 if (new_hash
!= NULL
) {
317 (v
->*link
).next
= *new_hash
;
318 if ((v
->*link
).next
!= NULL
) ((v
->*link
).next
->*link
).pprev
= &(v
->*link
).next
;
319 (v
->*link
).pprev
= new_hash
;
324 /* Forward declaration for HashAreaIterator */
325 template <uint nx
, uint ny
>
326 struct HashAreaIterator
;
329 * Hash pack template class
330 * @tparam nx Number of bits to use for x.
331 * @tparam ny Number of bits to use for y.
333 template <uint nx
, uint ny
>
335 static const uint PACK_BIT0_X
= 0;
336 static const uint PACK_BITS_X
= nx
;
337 static const uint PACK_MASK_X
= ((1 << PACK_BITS_X
) - 1) << PACK_BIT0_X
;
339 static const uint PACK_BIT0_Y
= PACK_BIT0_X
+ PACK_BITS_X
;
340 static const uint PACK_BITS_Y
= ny
;
341 static const uint PACK_MASK_Y
= ((1 << PACK_BITS_Y
) - 1) << PACK_BIT0_Y
;
343 static const uint PACK_BITS
= PACK_BIT0_Y
+ PACK_BITS_Y
;
344 static const uint PACK_SIZE
= 1 << PACK_BITS
;
346 typedef HashAreaIterator
<nx
, ny
> AreaIterator
;
348 static inline uint
pack_x (uint x
)
350 return (x
<< PACK_BIT0_X
) & PACK_MASK_X
;
353 static inline uint
pack_y (uint y
)
355 return (y
<< PACK_BIT0_Y
) & PACK_MASK_Y
;
358 static inline uint
pack (uint x
, uint y
)
360 return pack_x(x
) + pack_y(y
);
363 static inline uint
next_x (uint x
)
365 return (x
+ (1 << PACK_BIT0_X
)) & PACK_MASK_X
;
368 static inline uint
next_y (uint y
)
370 return (y
+ (1 << PACK_BIT0_Y
)) & PACK_MASK_Y
;
375 * Area iterator for a hash class
376 * @tparam nx Number of bits to use for x.
377 * @tparam ny Number of bits to use for y.
379 template <uint nx
, uint ny
>
380 struct HashAreaIterator
: HashPack
<nx
, ny
> {
384 void reset (uint xx0
, uint xx1
, uint yy0
, uint yy1
)
386 x0
= this->pack_x (xx0
);
387 x1
= this->pack_x (xx1
);
389 y0
= this->pack_y (yy0
);
390 y1
= this->pack_y (yy1
);
406 } else if (y
!= y1
) {
417 struct VehicleTileHash
: HashPack
<7, 7> {
418 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
419 * lookup times at the expense of memory usage. */
421 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
422 * Profiling results show that 0 is fastest. */
423 static const uint HASH_RES
= 0;
425 static const uint HASH_OFFSET_X
= HASH_RES
;
426 static const uint HASH_BITS_X
= PACK_BITS_X
;
428 static const uint HASH_OFFSET_Y
= HASH_RES
;
429 static const uint HASH_BITS_Y
= PACK_BITS_Y
;
431 static const uint HASH_SIZE
= PACK_SIZE
;
433 Vehicle
*buckets
[HASH_SIZE
];
435 static inline uint
hash (int x
, int y
)
437 return pack (GB(x
, HASH_OFFSET_X
, HASH_BITS_X
), GB(y
, HASH_OFFSET_Y
, HASH_BITS_Y
));
440 static inline uint
hash (TileIndex tile
)
442 return hash (TileX(tile
), TileY(tile
));
445 inline Vehicle
**get_bucket (TileIndex tile
)
447 return &this->buckets
[hash(tile
)];
450 void update (Vehicle
*v
, bool remove
= false)
452 Vehicle
**new_hash
= remove
? NULL
: get_bucket(v
->tile
);
454 UpdateVehicleHash(v
, &Vehicle::hash_tile_link
,
455 v
->hash_tile_current
, new_hash
);
457 /* Remember current hash position */
458 v
->hash_tile_current
= new_hash
;
464 FOR_ALL_VEHICLES(v
) { v
->hash_tile_current
= NULL
; }
465 memset (this->buckets
, 0, sizeof(this->buckets
));
468 void setup_iter (AreaIterator
*iter
, int xl
, int xu
, int yl
, int yu
)
470 iter
->reset (GB(xl
, HASH_OFFSET_X
, HASH_BITS_X
), GB(xu
, HASH_OFFSET_X
, HASH_BITS_X
),
471 GB(yl
, HASH_OFFSET_Y
, HASH_BITS_Y
), GB(yu
, HASH_OFFSET_Y
, HASH_BITS_Y
));
475 static VehicleTileHash vehicle_tile_hash
;
478 * Get the first vehicle for a VehicleTileIterator
479 * @param tile The tile to iterate at
481 Vehicle
*VehicleTileIterator::first (TileIndex tile
)
483 return *vehicle_tile_hash
.get_bucket(tile
);
488 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
489 * @note Do not call this function directly!
490 * @param x The X location on the map
491 * @param y The Y location on the map
492 * @param data Arbitrary data passed to proc
493 * @param proc The proc that determines whether a vehicle will be "found".
494 * @param find_first Whether to return on the first found or iterate over
496 * @return the best matching or first vehicle (depending on find_first).
498 static Vehicle
*VehicleFromPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
500 const int COLL_DIST
= 6;
502 /* Hash area to scan is from xl,yl to xu,yu */
503 VehicleTileHash::AreaIterator iter
;
504 vehicle_tile_hash
.setup_iter (&iter
,
505 (x
- COLL_DIST
) / TILE_SIZE
, (x
+ COLL_DIST
) / TILE_SIZE
,
506 (y
- COLL_DIST
) / TILE_SIZE
, (y
+ COLL_DIST
) / TILE_SIZE
);
509 Vehicle
*v
= vehicle_tile_hash
.buckets
[iter
.get()];
510 for (; v
!= NULL
; v
= v
->hash_tile_link
.next
) {
511 Vehicle
*a
= proc(v
, data
);
512 if (find_first
&& a
!= NULL
) return a
;
514 } while (iter
.next());
520 * Find a vehicle from a specific location. It will call proc for ALL vehicles
521 * on the tile and YOU must make SURE that the "best one" is stored in the
522 * data value and is ALWAYS the same regardless of the order of the vehicles
523 * where proc was called on!
524 * When you fail to do this properly you create an almost untraceable DESYNC!
525 * @note The return value of proc will be ignored.
526 * @note Use this when you have the intention that all vehicles
527 * should be iterated over.
528 * @param x The X location on the map
529 * @param y The Y location on the map
530 * @param data Arbitrary data passed to proc
531 * @param proc The proc that determines whether a vehicle will be "found".
533 void FindVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
535 VehicleFromPosXY(x
, y
, data
, proc
, false);
539 * Checks whether a vehicle in on a specific location. It will call proc for
540 * vehicles until it returns non-NULL.
541 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
542 * should be iterated over.
543 * @param x The X location on the map
544 * @param y The Y location on the map
545 * @param data Arbitrary data passed to proc
546 * @param proc The proc that determines whether a vehicle will be "found".
547 * @return True if proc returned non-NULL.
549 bool HasVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
551 return VehicleFromPosXY(x
, y
, data
, proc
, true) != NULL
;
555 * Ensure there is no vehicle at the ground at the given position.
556 * @param tile Position to examine.
557 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
559 CommandCost
EnsureNoVehicleOnGround(TileIndex tile
)
561 int z
= GetTileMaxPixelZ(tile
);
563 VehicleTileFinder
iter (tile
);
565 while (!iter
.finished()) {
568 if (v
->type
== VEH_DISASTER
|| (v
->type
== VEH_AIRCRAFT
&& v
->subtype
== AIR_SHADOW
)) continue;
569 if (v
->z_pos
> z
) continue;
574 /* Value v is not safe in MP games, however, it is used to generate a local
575 * error message only (which may be different for different machines).
576 * Such a message does not affect MP synchronisation.
578 if (iter
.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
579 return CommandCost();
583 * Finds vehicle in tunnel / bridge
584 * @param tile first end
585 * @param endtile second end
586 * @param ignore Ignore this vehicle when searching
587 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
589 CommandCost
TunnelBridgeIsFree(TileIndex tile
, TileIndex endtile
, const Vehicle
*ignore
)
591 /* Value v is not safe in MP games, however, it is used to generate a local
592 * error message only (which may be different for different machines).
593 * Such a message does not affect MP synchronisation.
597 VehicleTileFinder
iter1 (tile
);
598 while (!iter1
.finished()) {
601 if (v
->type
!= VEH_TRAIN
&& v
->type
!= VEH_ROAD
&& v
->type
!= VEH_SHIP
) continue;
602 if (v
== ignore
) continue;
606 if (iter1
.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
608 VehicleTileFinder
iter2 (tile
);
609 while (!iter2
.finished()) {
612 if (v
->type
!= VEH_TRAIN
&& v
->type
!= VEH_ROAD
&& v
->type
!= VEH_SHIP
) continue;
613 if (v
== ignore
) continue;
617 if (iter2
.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
619 return CommandCost();
622 static Track
AllowedNonOverlappingTrack(TrackBits bits
)
625 case TRACK_BIT_UPPER
: return TRACK_LOWER
;
626 case TRACK_BIT_LOWER
: return TRACK_UPPER
;
627 case TRACK_BIT_LEFT
: return TRACK_RIGHT
;
628 case TRACK_BIT_RIGHT
: return TRACK_LEFT
;
629 default: return INVALID_TRACK
;
634 * Tests if a vehicle interacts with the specified track bits.
635 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
637 * @param tile The tile.
638 * @param track_bits The track bits.
639 * @return \c true if no train that interacts, is found. \c false if a train is found.
641 CommandCost
EnsureNoTrainOnTrackBits(TileIndex tile
, TrackBits track_bits
)
643 assert(track_bits
!= TRACK_BIT_NONE
);
645 Track allowed
= AllowedNonOverlappingTrack(track_bits
);
647 VehicleTileFinder
iter (tile
);
648 while (!iter
.finished()) {
649 Vehicle
*v
= iter
.next();
650 if (v
->type
!= VEH_TRAIN
) continue;
652 Trackdir trackdir
= Train::From(v
)->trackdir
;
653 if (trackdir
>= TRACKDIR_END
) continue; // in wormhole or depot
655 if (TrackdirToTrack(trackdir
) != allowed
) iter
.set_found();
658 if (iter
.was_found()) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
);
659 return CommandCost();
662 static bool EnsureNoTrainOnBridgeEndTrackBits (TileIndex tile
, TrackBits bits
)
664 assert(bits
!= TRACK_BIT_NONE
);
666 Track allowed
= AllowedNonOverlappingTrack(bits
);
667 VehicleTileFinder
iter (tile
);
668 while (!iter
.finished()) {
669 Vehicle
*v
= iter
.next();
670 if (v
->type
!= VEH_TRAIN
) continue;
672 Trackdir trackdir
= Train::From(v
)->trackdir
;
673 if (TrackdirToTrack(trackdir
) != allowed
) iter
.set_found();
676 return !iter
.was_found();
680 * Tests if a train interacts with the specified track bits or is on the bridge middle part.
682 * @param tile1 one bridge end
683 * @param bits1 track bits on first bridge end
684 * @param tile2 the other bridge end
685 * @param bits2 track bits on second bridge end
686 * @return whether there is a train on the bridge tracks or middle part
688 CommandCost
EnsureNoTrainOnBridgeTrackBits(TileIndex tile1
, TrackBits bits1
, TileIndex tile2
, TrackBits bits2
)
690 if (!EnsureNoTrainOnBridgeEndTrackBits (tile1
, bits1
) ||
691 !EnsureNoTrainOnBridgeEndTrackBits (tile2
, bits2
)) {
692 return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
);
695 return CommandCost();
698 static bool EnsureNoTrainOnTunnelBridgeEndMiddle (TileIndex tile
)
700 VehicleTileFinder
iter (tile
);
701 while (!iter
.finished()) {
702 Vehicle
*v
= iter
.next();
703 if (v
->type
== VEH_TRAIN
&& Train::From(v
)->trackdir
== TRACKDIR_WORMHOLE
) iter
.set_found();
705 return !iter
.was_found();
709 * Tests if there is a train on the middle bridge part
710 * @param tile1 one bridge end
711 * @param tile2 the other bridge end
712 * @return whether there is a train on the bridge
714 CommandCost
EnsureNoTrainOnTunnelBridgeMiddle(TileIndex tile1
, TileIndex tile2
)
716 if (!EnsureNoTrainOnTunnelBridgeEndMiddle(tile1
) ||
717 !EnsureNoTrainOnTunnelBridgeEndMiddle(tile2
)) {
718 return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
);
721 return CommandCost();
725 struct VehicleViewportHash
: HashPack
<6, 6> {
726 static const uint HASH_OFFSET_X
= 7 + ZOOM_LVL_SHIFT
;
727 static const uint HASH_BITS_X
= PACK_BITS_X
;
729 static const uint HASH_OFFSET_Y
= 6 + ZOOM_LVL_SHIFT
;
730 static const uint HASH_BITS_Y
= PACK_BITS_Y
;
732 static const uint HASH_SIZE
= PACK_SIZE
;
734 Vehicle
*buckets
[HASH_SIZE
];
736 static inline uint
hash (int x
, int y
)
738 return pack (GB(x
, HASH_OFFSET_X
, HASH_BITS_X
), GB(y
, HASH_OFFSET_Y
, HASH_BITS_Y
));
741 inline Vehicle
**get_bucket (int x
, int y
)
743 return (x
== INVALID_COORD
) ? NULL
: &buckets
[hash(x
, y
)];
746 void update (Vehicle
*v
, int x
, int y
)
748 UpdateVehicleHash(v
, &Vehicle::hash_viewport_link
,
749 this->get_bucket (v
->coord
.left
, v
->coord
.top
),
750 this->get_bucket (x
, y
));
755 memset (this->buckets
, 0, sizeof(this->buckets
));
758 void setup_iter (AreaIterator
*iter
, int l
, uint w
, int t
, uint h
)
762 if (w
< (1 << (HASH_OFFSET_X
+ HASH_BITS_X
))) {
763 x0
= GB(l
, HASH_OFFSET_X
, HASH_BITS_X
);
764 x1
= GB(l
+ w
, HASH_OFFSET_X
, HASH_BITS_X
);
766 /* scan whole hash row */
768 x1
= (1 << HASH_BITS_X
) - 1;
771 if (h
< (1 << (HASH_OFFSET_Y
+ HASH_BITS_Y
))) {
772 y0
= GB(t
, HASH_OFFSET_Y
, HASH_BITS_Y
);
773 y1
= GB(t
+ h
, HASH_OFFSET_Y
, HASH_BITS_Y
);
775 /* scan whole column */
777 y1
= (1 << HASH_BITS_Y
) - 1;
780 iter
->reset (x0
, x1
, y0
, y1
);
785 static VehicleViewportHash vehicle_viewport_hash
;
787 void ResetVehicleHash()
789 vehicle_viewport_hash
.reset();
790 vehicle_tile_hash
.reset();
793 void ResetVehicleColourMap()
796 FOR_ALL_VEHICLES(v
) { v
->colourmap
= PAL_NONE
; }
800 * List of vehicles that should check for autoreplace this tick.
801 * Mapping of vehicle -> leave depot immediately after autoreplace.
803 typedef SmallMap
<Vehicle
*, bool, 4> AutoreplaceMap
;
804 static AutoreplaceMap _vehicles_to_autoreplace
;
806 void InitializeVehicles()
808 _vehicles_to_autoreplace
.Reset();
812 uint
CountVehiclesInChain(const Vehicle
*v
)
815 do count
++; while ((v
= v
->Next()) != NULL
);
820 * Check if a vehicle is counted in num_engines in each company struct
821 * @return true if the vehicle is counted in num_engines
823 bool Vehicle::IsEngineCountable() const
825 switch (this->type
) {
826 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
828 return !this->IsArticulatedPart() && // tenders and other articulated parts
829 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
830 case VEH_ROAD
: return RoadVehicle::From(this)->IsFrontEngine();
831 case VEH_SHIP
: return true;
832 default: return false; // Only count company buildable vehicles
837 * Check whether Vehicle::engine_type has any meaning.
838 * @return true if the vehicle has a useable engine type.
840 bool Vehicle::HasEngineType() const
842 switch (this->type
) {
843 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft();
846 case VEH_SHIP
: return true;
847 default: return false;
852 * Retrieves the engine of the vehicle.
853 * @return Engine of the vehicle.
854 * @pre HasEngineType() == true
856 const Engine
*Vehicle::GetEngine() const
858 return Engine::Get(this->engine_type
);
862 * Retrieve the NewGRF the vehicle is tied to.
863 * This is the GRF providing the Action 3 for the engine type.
864 * @return NewGRF associated to the vehicle.
866 const GRFFile
*Vehicle::GetGRF() const
868 return this->GetEngine()->GetGRF();
872 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
873 * This is the GRF providing the Action 3 for the engine type.
874 * @return GRF ID of the associated NewGRF.
876 uint32
Vehicle::GetGRFID() const
878 return this->GetEngine()->GetGRFID();
882 * Handle the pathfinding result, especially the lost status.
883 * If the vehicle is now lost and wasn't previously fire an
884 * event to the AIs and a news message to the user. If the
885 * vehicle is not lost anymore remove the news message.
886 * @param path_found Whether the vehicle has a path to its destination.
888 void Vehicle::HandlePathfindingResult(bool path_found
)
891 /* Route found, is the vehicle marked with "lost" flag? */
892 if (!HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
894 /* Clear the flag as the PF's problem was solved. */
895 ClrBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
896 /* Delete the news item. */
897 DeleteVehicleNews(this->index
, STR_NEWS_VEHICLE_IS_LOST
);
901 /* Were we already lost? */
902 if (HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
904 /* It is first time the problem occurred, set the "lost" flag. */
905 SetBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
906 /* Notify user about the event. */
907 AI::NewEvent(this->owner
, new ScriptEventVehicleLost(this->index
));
908 if (_settings_client
.gui
.lost_vehicle_warn
&& this->owner
== _local_company
) {
909 SetDParam(0, this->index
);
910 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST
, this->index
);
914 /** Destroy all stuff that (still) needs the virtual functions to work properly */
915 void Vehicle::PreDestructor()
917 if (CleaningPool()) return;
919 if (Station::IsValidID(this->last_station_visited
)) {
920 Station
*st
= Station::Get(this->last_station_visited
);
921 st
->loading_vehicles
.remove(this);
923 HideFillingPercent(&this->fill_percent_te_id
);
924 this->CancelReservation(INVALID_STATION
, st
);
925 delete this->cargo_payment
;
928 if (this->IsEngineCountable()) {
929 GroupStatistics::CountEngine(this, -1);
930 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
931 GroupStatistics::UpdateAutoreplace(this->owner
);
933 if (this->owner
== _local_company
) InvalidateAutoreplaceWindow(this->engine_type
, this->group_id
);
934 DeleteGroupHighlightOfVehicle(this);
937 if (this->type
== VEH_AIRCRAFT
&& this->IsPrimaryVehicle()) {
938 Aircraft
*a
= Aircraft::From(this);
939 Station
*st
= GetTargetAirportIfValid(a
);
941 const AirportFTA
*layout
= st
->airport
.GetFTA()->layout
;
942 CLRBITS(st
->airport
.flags
, layout
[a
->previous_pos
].block
| layout
[a
->pos
].block
);
947 if (this->type
== VEH_ROAD
&& this->IsPrimaryVehicle()) {
948 RoadVehicle
*v
= RoadVehicle::From(this);
949 if (!(v
->vehstatus
& VS_CRASHED
) && IsInsideMM(v
->state
, RVSB_IN_DT_ROAD_STOP
, RVSB_IN_DT_ROAD_STOP_END
)) {
950 /* Leave the drive through roadstop, when you have not already left it. */
951 RoadStop::GetByTile(v
->tile
, GetRoadStopType(v
->tile
))->Leave(v
);
955 if (this->Previous() == NULL
) {
956 InvalidateWindowData(WC_VEHICLE_DEPOT
, this->tile
);
959 if (this->IsPrimaryVehicle()) {
960 DeleteWindowById(WC_VEHICLE_VIEW
, this->index
);
961 DeleteWindowById(WC_VEHICLE_ORDERS
, this->index
);
962 DeleteWindowById(WC_VEHICLE_REFIT
, this->index
);
963 DeleteWindowById(WC_VEHICLE_DETAILS
, this->index
);
964 DeleteWindowById(WC_VEHICLE_TIMETABLE
, this->index
);
965 SetWindowDirty(WC_COMPANY
, this->owner
);
966 OrderBackup::ClearVehicle(this);
968 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
970 this->cargo
.Truncate();
971 DeleteVehicleOrders(this);
972 DeleteDepotHighlightOfVehicle(this);
974 extern void StopGlobalFollowVehicle(const Vehicle
*v
);
975 StopGlobalFollowVehicle(this);
977 ReleaseDisastersTargetingVehicle(this->index
);
982 if (CleaningPool()) {
983 this->cargo
.OnCleanPool();
987 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
988 * it may happen that vehicle chain is deleted when visible */
989 if (!(this->vehstatus
& VS_HIDDEN
)) MarkSingleVehicleDirty(this);
991 Vehicle
*v
= this->Next();
996 vehicle_tile_hash
.update (this, true);
997 vehicle_viewport_hash
.update (this, INVALID_COORD
, 0);
998 DeleteVehicleNews(this->index
, INVALID_STRING_ID
);
999 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type
), this->index
);
1003 * Adds a vehicle to the list of vehicles that visited a depot this tick
1004 * @param *v vehicle to add
1006 void VehicleEnteredDepotThisTick(Vehicle
*v
)
1008 /* Vehicle should stop in the depot if it was in 'stopping' state */
1009 _vehicles_to_autoreplace
[v
] = !(v
->vehstatus
& VS_STOPPED
);
1011 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
1012 * stopping in the depot, so we stop it to ensure that it will not reserve
1013 * the path out of the depot before we might autoreplace it to a different
1014 * engine. The new engine would not own the reserved path we store that we
1015 * stopped the vehicle, so autoreplace can start it again */
1016 v
->vehstatus
|= VS_STOPPED
;
1020 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
1021 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
1022 * so each day, all vehicles are processes in DAY_TICKS steps.
1024 static void RunVehicleDayProc()
1026 if (_game_mode
!= GM_NORMAL
) return;
1028 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1029 for (size_t i
= _date_fract
; i
< Vehicle::GetPoolSize(); i
+= DAY_TICKS
) {
1030 Vehicle
*v
= Vehicle::Get(i
);
1031 if (v
== NULL
) continue;
1033 /* Call the 32-day callback if needed */
1034 if ((v
->day_counter
& 0x1F) == 0 && v
->HasEngineType()) {
1035 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK
, 0, 0, v
->engine_type
, v
);
1036 if (callback
!= CALLBACK_FAILED
) {
1037 if (HasBit(callback
, 0)) {
1038 TriggerVehicle(v
, VEHICLE_TRIGGER_CALLBACK_32
); // Trigger vehicle trigger 10
1041 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1042 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1043 if (callback
!= 0) v
->First()->MarkDirty();
1045 if (callback
& ~3) ErrorUnknownCallbackResult(v
->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK
, callback
);
1049 /* This is called once per day for each vehicle, but not in the first tick of the day */
1054 void CallVehicleTicks()
1056 _vehicles_to_autoreplace
.Clear();
1058 RunVehicleDayProc();
1061 FOR_ALL_STATIONS(st
) LoadUnloadStation(st
);
1064 FOR_ALL_VEHICLES(v
) {
1065 /* Vehicle could be deleted in this tick */
1067 assert(Vehicle::Get(vehicle_index
) == NULL
);
1071 assert(Vehicle::Get(vehicle_index
) == v
);
1080 Vehicle
*front
= v
->First();
1082 if (v
->vcache
.cached_cargo_age_period
!= 0) {
1083 v
->cargo_age_counter
= min(v
->cargo_age_counter
, v
->vcache
.cached_cargo_age_period
);
1084 if (--v
->cargo_age_counter
== 0) {
1085 v
->cargo
.AgeCargo();
1086 v
->cargo_age_counter
= v
->vcache
.cached_cargo_age_period
;
1090 /* Do not play any sound when crashed */
1091 if (front
->vehstatus
& VS_CRASHED
) continue;
1093 /* Do not play any sound when in depot or tunnel */
1094 if (v
->vehstatus
& VS_HIDDEN
) continue;
1096 /* Do not play any sound when stopped */
1097 if ((front
->vehstatus
& VS_STOPPED
) && (front
->type
!= VEH_TRAIN
|| front
->cur_speed
== 0)) continue;
1099 /* Check vehicle type specifics */
1102 if (Train::From(v
)->IsWagon()) continue;
1106 if (!RoadVehicle::From(v
)->IsFrontEngine()) continue;
1110 if (!Aircraft::From(v
)->IsNormalAircraft()) continue;
1117 v
->motion_counter
+= front
->cur_speed
;
1118 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1119 if (GB(v
->motion_counter
, 0, 8) < front
->cur_speed
) PlayVehicleSound(v
, VSE_RUNNING
);
1121 /* Play an alternating running sound every 16 ticks */
1122 if (GB(v
->tick_counter
, 0, 4) == 0) {
1123 /* Play running sound when speed > 0 and not braking */
1124 bool running
= (front
->cur_speed
> 0) && !(front
->vehstatus
& (VS_STOPPED
| VS_TRAIN_SLOWING
));
1125 PlayVehicleSound(v
, running
? VSE_RUNNING_16
: VSE_STOPPED_16
);
1133 Backup
<CompanyByte
> cur_company(_current_company
, FILE_LINE
);
1134 for (AutoreplaceMap::iterator it
= _vehicles_to_autoreplace
.Begin(); it
!= _vehicles_to_autoreplace
.End(); it
++) {
1136 /* Autoreplace needs the current company set as the vehicle owner */
1137 cur_company
.Change(v
->owner
);
1139 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1140 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1141 * they are already leaving the depot again before being replaced. */
1142 if (it
->second
) v
->vehstatus
&= ~VS_STOPPED
;
1144 /* Store the position of the effect as the vehicle pointer will become invalid later */
1149 const Company
*c
= Company::Get(_current_company
);
1150 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, (Money
)c
->settings
.engine_renew_money
));
1151 CommandCost res
= DoCommand(0, v
->index
, 0, DC_EXEC
, CMD_AUTOREPLACE_VEHICLE
);
1152 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, -(Money
)c
->settings
.engine_renew_money
));
1154 if (!IsLocalCompany()) continue;
1156 if (res
.Succeeded()) {
1157 ShowCostOrIncomeAnimation(x
, y
, z
, res
.GetCost());
1161 StringID error_message
= res
.GetErrorMessage();
1162 if (error_message
== STR_ERROR_AUTOREPLACE_NOTHING_TO_DO
|| error_message
== INVALID_STRING_ID
) continue;
1164 if (error_message
== STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY
) error_message
= STR_ERROR_AUTOREPLACE_MONEY_LIMIT
;
1167 if (error_message
== STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT
) {
1168 message
= error_message
;
1170 message
= STR_NEWS_VEHICLE_AUTORENEW_FAILED
;
1173 SetDParam(0, v
->index
);
1174 SetDParam(1, error_message
);
1175 AddVehicleAdviceNewsItem(message
, v
->index
);
1178 cur_company
.Restore();
1182 * Add vehicle sprite for drawing to the screen.
1183 * @param v Vehicle to draw.
1185 static void DoDrawVehicle(const Vehicle
*v
)
1187 SpriteID image
= v
->cur_image
;
1188 PaletteID pal
= PAL_NONE
;
1190 if (v
->vehstatus
& VS_DEFPAL
) pal
= (v
->vehstatus
& VS_CRASHED
) ? PALETTE_CRASH
: GetVehiclePalette(v
);
1192 /* Check whether the vehicle shall be transparent due to the game state */
1193 bool shadowed
= (v
->vehstatus
& VS_SHADOW
) != 0;
1195 if (v
->type
== VEH_EFFECT
) {
1196 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1197 * However, transparent smoke and bubbles look weird, so always hide them. */
1198 TransparencyOption to
= EffectVehicle::From(v
)->GetTransparencyOption();
1199 if (to
!= TO_INVALID
&& (IsTransparencySet(to
) || IsInvisibilitySet(to
))) return;
1202 AddSortableSpriteToDraw(image
, pal
, v
->x_pos
+ v
->x_offs
, v
->y_pos
+ v
->y_offs
,
1203 v
->x_extent
, v
->y_extent
, v
->z_extent
, v
->z_pos
, shadowed
, v
->x_bb_offs
, v
->y_bb_offs
);
1207 * Add the vehicle sprites that should be drawn at a part of the screen.
1208 * @param dpi Rectangle being drawn.
1210 void ViewportAddVehicles(const DrawPixelInfo
*dpi
)
1212 /* The bounding rectangle */
1213 const int l
= dpi
->left
;
1214 const int r
= dpi
->left
+ dpi
->width
;
1215 const int t
= dpi
->top
;
1216 const int b
= dpi
->top
+ dpi
->height
;
1218 /* The hash area to scan */
1219 VehicleViewportHash::AreaIterator iter
;
1220 vehicle_viewport_hash
.setup_iter (&iter
,
1221 l
- (70 * ZOOM_LVL_BASE
), dpi
->width
+ (70 * ZOOM_LVL_BASE
),
1222 t
- (70 * ZOOM_LVL_BASE
), dpi
->height
+ (70 * ZOOM_LVL_BASE
));
1225 const Vehicle
*v
= vehicle_viewport_hash
.buckets
[iter
.get()];
1228 if (!(v
->vehstatus
& VS_HIDDEN
) &&
1229 l
<= v
->coord
.right
&&
1230 t
<= v
->coord
.bottom
&&
1231 r
>= v
->coord
.left
&&
1232 b
>= v
->coord
.top
) {
1235 v
= v
->hash_viewport_link
.next
;
1238 } while (iter
.next());
1242 * Find the vehicle close to the clicked coordinates.
1243 * @param vp Viewport clicked in.
1244 * @param x X coordinate in the viewport.
1245 * @param y Y coordinate in the viewport.
1246 * @return Closest vehicle, or \c NULL if none found.
1248 Vehicle
*CheckClickOnVehicle(const ViewPort
*vp
, int x
, int y
)
1250 Vehicle
*found
= NULL
, *v
;
1251 uint dist
, best_dist
= UINT_MAX
;
1253 if ((uint
)(x
-= vp
->left
) >= (uint
)vp
->width
|| (uint
)(y
-= vp
->top
) >= (uint
)vp
->height
) return NULL
;
1255 x
= ScaleByZoom(x
, vp
->zoom
) + vp
->virtual_left
;
1256 y
= ScaleByZoom(y
, vp
->zoom
) + vp
->virtual_top
;
1258 FOR_ALL_VEHICLES(v
) {
1259 if ((v
->vehstatus
& (VS_HIDDEN
| VS_UNCLICKABLE
)) == 0 &&
1260 x
>= v
->coord
.left
&& x
<= v
->coord
.right
&&
1261 y
>= v
->coord
.top
&& y
<= v
->coord
.bottom
) {
1264 abs(((v
->coord
.left
+ v
->coord
.right
) >> 1) - x
),
1265 abs(((v
->coord
.top
+ v
->coord
.bottom
) >> 1) - y
)
1268 if (dist
< best_dist
) {
1279 * Decrease the value of a vehicle.
1280 * @param v %Vehicle to devaluate.
1282 void DecreaseVehicleValue(Vehicle
*v
)
1284 v
->value
-= v
->value
>> 8;
1285 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1288 static const byte _breakdown_chance
[64] = {
1289 3, 3, 3, 3, 3, 3, 3, 3,
1290 4, 4, 5, 5, 6, 6, 7, 7,
1291 8, 8, 9, 9, 10, 10, 11, 11,
1292 12, 13, 13, 13, 13, 14, 15, 16,
1293 17, 19, 21, 25, 28, 31, 34, 37,
1294 40, 44, 48, 52, 56, 60, 64, 68,
1295 72, 80, 90, 100, 110, 120, 130, 140,
1296 150, 170, 190, 210, 230, 250, 250, 250,
1299 void CheckVehicleBreakdown(Vehicle
*v
)
1303 /* decrease reliability */
1304 v
->reliability
= rel
= max((rel_old
= v
->reliability
) - v
->reliability_spd_dec
, 0);
1305 if ((rel_old
>> 8) != (rel
>> 8)) SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1307 if (v
->breakdown_ctr
!= 0 || (v
->vehstatus
& VS_STOPPED
) ||
1308 _settings_game
.difficulty
.vehicle_breakdowns
< 1 ||
1309 v
->cur_speed
< 5 || _game_mode
== GM_MENU
) {
1313 uint32 r
= Random();
1315 /* increase chance of failure */
1316 int chance
= v
->breakdown_chance
+ 1;
1317 if (Chance16I(1, 25, r
)) chance
+= 25;
1318 v
->breakdown_chance
= min(255, chance
);
1320 /* calculate reliability value to use in comparison */
1321 rel
= v
->reliability
;
1322 if (v
->type
== VEH_SHIP
) rel
+= 0x6666;
1324 /* reduced breakdowns? */
1325 if (_settings_game
.difficulty
.vehicle_breakdowns
== 1) rel
+= 0x6666;
1327 /* check if to break down */
1328 if (_breakdown_chance
[(uint
)min(rel
, 0xffff) >> 10] <= v
->breakdown_chance
) {
1329 v
->breakdown_ctr
= GB(r
, 16, 6) + 0x3F;
1330 v
->breakdown_delay
= GB(r
, 24, 7) + 0x80;
1331 v
->breakdown_chance
= 0;
1336 * Handle all of the aspects of a vehicle breakdown
1337 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1338 * @return true iff the vehicle is stopped because of a breakdown
1339 * @note This function always returns false for aircraft, since these never stop for breakdowns
1341 bool Vehicle::HandleBreakdown()
1343 /* Possible states for Vehicle::breakdown_ctr
1344 * 0 - vehicle is running normally
1345 * 1 - vehicle is currently broken down
1346 * 2 - vehicle is going to break down now
1347 * >2 - vehicle is counting down to the actual breakdown event */
1348 switch (this->breakdown_ctr
) {
1353 this->breakdown_ctr
= 1;
1355 if (this->breakdowns_since_last_service
!= 255) {
1356 this->breakdowns_since_last_service
++;
1359 if (this->type
== VEH_AIRCRAFT
) {
1360 /* Aircraft just need this flag, the rest is handled elsewhere */
1361 this->vehstatus
|= VS_AIRCRAFT_BROKEN
;
1363 this->cur_speed
= 0;
1365 if (!PlayVehicleSound(this, VSE_BREAKDOWN
)) {
1366 SndPlayVehicleFx((_settings_game
.game_creation
.landscape
!= LT_TOYLAND
) ?
1367 (this->type
== VEH_TRAIN
? SND_10_TRAIN_BREAKDOWN
: SND_0F_VEHICLE_BREAKDOWN
) :
1368 (this->type
== VEH_TRAIN
? SND_3A_COMEDY_BREAKDOWN_2
: SND_35_COMEDY_BREAKDOWN
), this);
1371 if (!(this->vehstatus
& VS_HIDDEN
) && !HasBit(EngInfo(this->engine_type
)->misc_flags
, EF_NO_BREAKDOWN_SMOKE
)) {
1372 EffectVehicle
*u
= CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE
);
1373 if (u
!= NULL
) u
->animation_state
= this->breakdown_delay
* 2;
1377 this->MarkDirty(); // Update graphics after speed is zeroed
1378 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1379 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
1383 /* Aircraft breakdowns end only when arriving at the airport */
1384 if (this->type
== VEH_AIRCRAFT
) return false;
1386 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1387 if ((this->tick_counter
& (this->type
== VEH_TRAIN
? 3 : 1)) == 0) {
1388 if (--this->breakdown_delay
== 0) {
1389 this->breakdown_ctr
= 0;
1391 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1397 if (!this->current_order
.IsType(OT_LOADING
)) this->breakdown_ctr
--;
1403 * Update age of a vehicle.
1404 * @param v Vehicle to update.
1406 void AgeVehicle(Vehicle
*v
)
1408 if (v
->age
< MAX_DAY
) {
1410 if (v
->IsPrimaryVehicle() && v
->age
== VEHICLE_PROFIT_MIN_AGE
+ 1) GroupStatistics::VehicleReachedProfitAge(v
);
1413 if (!v
->IsPrimaryVehicle() && (v
->type
!= VEH_TRAIN
|| !Train::From(v
)->IsEngine())) return;
1415 int age
= v
->age
- v
->max_age
;
1416 if (age
== DAYS_IN_LEAP_YEAR
* 0 || age
== DAYS_IN_LEAP_YEAR
* 1 ||
1417 age
== DAYS_IN_LEAP_YEAR
* 2 || age
== DAYS_IN_LEAP_YEAR
* 3 || age
== DAYS_IN_LEAP_YEAR
* 4) {
1418 v
->reliability_spd_dec
<<= 1;
1421 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1423 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1424 if (v
->Previous() != NULL
|| v
->owner
!= _local_company
|| (v
->vehstatus
& VS_CRASHED
) != 0) return;
1426 /* Don't warn if a renew is active */
1427 if (Company::Get(v
->owner
)->settings
.engine_renew
&& v
->GetEngine()->company_avail
!= 0) return;
1430 if (age
== -DAYS_IN_LEAP_YEAR
) {
1431 str
= STR_NEWS_VEHICLE_IS_GETTING_OLD
;
1432 } else if (age
== 0) {
1433 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD
;
1434 } else if (age
> 0 && (age
% DAYS_IN_LEAP_YEAR
) == 0) {
1435 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND
;
1440 SetDParam(0, v
->index
);
1441 AddVehicleAdviceNewsItem(str
, v
->index
);
1445 * Calculates how full a vehicle is.
1446 * @param front The front vehicle of the consist to check.
1447 * @param colour The string to show depending on if we are unloading or loading
1448 * @return A percentage of how full the Vehicle is.
1450 uint8
CalcPercentVehicleFilled(const Vehicle
*front
, StringID
*colour
)
1456 bool loading
= false;
1458 bool is_loading
= front
->current_order
.IsType(OT_LOADING
);
1460 /* The station may be NULL when the (colour) string does not need to be set. */
1461 const Station
*st
= Station::GetIfValid(front
->last_station_visited
);
1462 assert(colour
== NULL
|| (st
!= NULL
&& is_loading
));
1464 bool order_no_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_NO_LOAD
);
1465 bool order_full_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_FULL_LOAD
);
1467 /* Count up max and used */
1468 for (const Vehicle
*v
= front
; v
!= NULL
; v
= v
->Next()) {
1469 count
+= v
->cargo
.StoredCount();
1470 max
+= v
->cargo_cap
;
1471 if (v
->cargo_cap
!= 0 && colour
!= NULL
) {
1472 unloading
+= HasBit(v
->vehicle_flags
, VF_CARGO_UNLOADING
) ? 1 : 0;
1473 loading
|= !order_no_load
&&
1474 (order_full_load
|| st
->goods
[v
->cargo_type
].HasRating()) &&
1475 !HasBit(v
->vehicle_flags
, VF_LOADING_FINISHED
) && !HasBit(v
->vehicle_flags
, VF_STOP_LOADING
);
1480 if (colour
!= NULL
) {
1481 if (unloading
== 0 && loading
) {
1482 *colour
= STR_PERCENT_UP
;
1483 } else if (unloading
== 0 && !loading
) {
1484 *colour
= STR_PERCENT_NONE
;
1485 } else if (cars
== unloading
|| !loading
) {
1486 *colour
= STR_PERCENT_DOWN
;
1488 *colour
= STR_PERCENT_UP_DOWN
;
1492 /* Train without capacity */
1493 if (max
== 0) return 100;
1495 /* Return the percentage */
1496 return (count
* 100) / max
;
1500 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1501 * @param v Vehicle that entered a depot.
1503 void VehicleEnterDepot(Vehicle
*v
)
1505 /* Always work with the front of the vehicle */
1506 assert(v
== v
->First());
1510 Train
*t
= Train::From(v
);
1511 SetWindowClassesDirty(WC_TRAINS_LIST
);
1512 /* Clear path reservation */
1513 SetDepotReservation(t
->tile
, false);
1514 if (_settings_client
.gui
.show_track_reservation
) MarkTileDirtyByTile(t
->tile
);
1516 assert(IsSignalBufferEmpty());
1517 AddDepotToSignalBuffer(t
->tile
, t
->owner
);
1518 UpdateSignalsInBuffer();
1519 t
->wait_counter
= 0;
1520 t
->force_proceed
= TFP_NONE
;
1521 ClrBit(t
->flags
, VRF_TOGGLE_REVERSE
);
1522 t
->ConsistChanged(true);
1527 SetWindowClassesDirty(WC_ROADVEH_LIST
);
1531 SetWindowClassesDirty(WC_SHIPS_LIST
);
1532 Ship
*ship
= Ship::From(v
);
1533 ship
->trackdir
= TRACKDIR_DEPOT
;
1534 ship
->UpdateCache();
1535 ship
->UpdateViewport(true, true);
1536 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1541 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
1542 HandleAircraftEnterHangar(Aircraft::From(v
));
1544 default: NOT_REACHED();
1546 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1548 if (v
->type
!= VEH_TRAIN
) {
1549 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1550 * 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 */
1551 InvalidateWindowData(WC_VEHICLE_DEPOT
, v
->tile
);
1553 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1555 v
->vehstatus
|= VS_HIDDEN
;
1558 VehicleServiceInDepot(v
);
1560 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1561 TriggerVehicle(v
, VEHICLE_TRIGGER_DEPOT
);
1564 if (v
->current_order
.IsType(OT_GOTO_DEPOT
)) {
1565 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1567 const Order
*real_order
= v
->GetOrder(v
->cur_real_order_index
);
1568 Order t
= v
->current_order
;
1569 v
->current_order
.MakeDummy();
1571 /* Test whether we are heading for this depot. If not, do nothing.
1572 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1573 if ((t
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) &&
1574 real_order
!= NULL
&& !(real_order
->GetDepotActionType() & ODATFB_NEAREST_DEPOT
) &&
1575 (v
->type
== VEH_AIRCRAFT
? t
.GetDestination() != GetStationIndex(v
->tile
) : v
->dest_tile
!= v
->tile
)) {
1576 /* We are heading for another depot, keep driving. */
1581 Backup
<CompanyByte
> cur_company(_current_company
, v
->owner
, FILE_LINE
);
1582 CommandCost cost
= DoCommand(v
->tile
, v
->index
, t
.GetRefitCargo() | 0xFF << 8, DC_EXEC
, GetCmdRefitVeh(v
));
1583 cur_company
.Restore();
1585 if (cost
.Failed()) {
1586 _vehicles_to_autoreplace
[v
] = false;
1587 if (v
->owner
== _local_company
) {
1588 /* Notify the user that we stopped the vehicle */
1589 SetDParam(0, v
->index
);
1590 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED
, v
->index
);
1592 } else if (cost
.GetCost() != 0) {
1593 v
->profit_this_year
-= cost
.GetCost() << 8;
1594 if (v
->owner
== _local_company
) {
1595 ShowCostOrIncomeAnimation(v
->x_pos
, v
->y_pos
, v
->z_pos
, cost
.GetCost());
1600 if (t
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) {
1601 /* Part of orders */
1602 v
->DeleteUnreachedImplicitOrders();
1603 UpdateVehicleTimetable(v
, true);
1604 v
->IncrementImplicitOrderIndex();
1606 if (t
.GetDepotActionType() & ODATFB_HALT
) {
1607 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1608 _vehicles_to_autoreplace
[v
] = false;
1609 /* Invalidate last_loading_station. As the link from the station
1610 * before the stop to the station after the stop can't be predicted
1611 * we shouldn't construct it when the vehicle visits the next stop. */
1612 v
->last_loading_station
= INVALID_STATION
;
1613 if (v
->owner
== _local_company
) {
1614 SetDParam(0, v
->index
);
1615 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING
+ v
->type
, v
->index
);
1617 AI::NewEvent(v
->owner
, new ScriptEventVehicleWaitingInDepot(v
->index
));
1624 * Update the position of the vehicle. This will update the hash that tells
1625 * which vehicles are on a tile.
1626 * @param v The vehicle to update.
1628 void VehicleUpdatePosition(Vehicle
*v
)
1630 vehicle_tile_hash
.update(v
);
1634 * Update the vehicle on the viewport, updating the right hash and setting the
1636 * @param v The vehicle to update.
1637 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1639 void VehicleUpdateViewport(Vehicle
*v
, bool dirty
)
1641 int img
= v
->cur_image
;
1642 Point pt
= RemapCoords(v
->x_pos
+ v
->x_offs
, v
->y_pos
+ v
->y_offs
, v
->z_pos
);
1643 const Sprite
*spr
= GetSprite(img
, ST_NORMAL
);
1645 pt
.x
+= spr
->x_offs
;
1646 pt
.y
+= spr
->y_offs
;
1648 vehicle_viewport_hash
.update (v
, pt
.x
, pt
.y
);
1650 Rect old_coord
= v
->coord
;
1651 v
->coord
.left
= pt
.x
;
1652 v
->coord
.top
= pt
.y
;
1653 v
->coord
.right
= pt
.x
+ spr
->width
+ 2 * ZOOM_LVL_BASE
;
1654 v
->coord
.bottom
= pt
.y
+ spr
->height
+ 2 * ZOOM_LVL_BASE
;
1657 if (old_coord
.left
== INVALID_COORD
) {
1658 MarkSingleVehicleDirty(v
);
1660 MarkAllViewportsDirty(
1661 min(old_coord
.left
, v
->coord
.left
),
1662 min(old_coord
.top
, v
->coord
.top
),
1663 max(old_coord
.right
, v
->coord
.right
) + 1 * ZOOM_LVL_BASE
,
1664 max(old_coord
.bottom
, v
->coord
.bottom
) + 1 * ZOOM_LVL_BASE
1671 * Update the position of the vehicle, and update the viewport.
1672 * @param v The vehicle to update.
1674 void VehicleUpdatePositionAndViewport(Vehicle
*v
)
1676 VehicleUpdatePosition(v
);
1677 VehicleUpdateViewport(v
, true);
1681 * Marks viewports dirty where the vehicle's image is.
1682 * @param v vehicle to mark dirty
1684 void MarkSingleVehicleDirty(const Vehicle
*v
)
1686 MarkAllViewportsDirty(v
->coord
.left
, v
->coord
.top
, v
->coord
.right
+ 1 * ZOOM_LVL_BASE
, v
->coord
.bottom
+ 1 * ZOOM_LVL_BASE
);
1690 * Get position information of a vehicle when moving one pixel in the direction it is facing
1691 * @param v Vehicle to move
1692 * @return Position information after the move
1694 VehiclePos
GetNewVehiclePos(const Vehicle
*v
)
1696 static const int8 delta_coord
[DIR_END
][2] = { /* {x,y} */
1697 {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}
1701 gp
.x
= v
->x_pos
+ delta_coord
[v
->direction
][0];
1702 gp
.y
= v
->y_pos
+ delta_coord
[v
->direction
][1];
1703 gp
.new_tile
= TileVirtXY(gp
.x
, gp
.y
);
1707 static const Direction _new_direction_table
[] = {
1708 DIR_N
, DIR_NW
, DIR_W
,
1709 DIR_NE
, DIR_SE
, DIR_SW
,
1710 DIR_E
, DIR_SE
, DIR_S
1713 Direction
GetDirectionTowards(const Vehicle
*v
, int x
, int y
)
1717 if (y
>= v
->y_pos
) {
1718 if (y
!= v
->y_pos
) i
+= 3;
1722 if (x
>= v
->x_pos
) {
1723 if (x
!= v
->x_pos
) i
++;
1727 Direction dir
= v
->direction
;
1729 DirDiff dirdiff
= DirDifference(_new_direction_table
[i
], dir
);
1730 if (dirdiff
== DIRDIFF_SAME
) return dir
;
1731 return ChangeDir(dir
, dirdiff
> DIRDIFF_REVERSE
? DIRDIFF_45LEFT
: DIRDIFF_45RIGHT
);
1735 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1736 * struct initialization, except after each call to this->NextID() the returned value
1737 * is assigned to a vehicle.
1738 * @param type type of vehicle
1739 * @param owner owner of vehicles
1741 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type
, CompanyID owner
) : cache(NULL
), maxid(0), curid(0)
1745 FOR_ALL_VEHICLES(v
) {
1746 if (v
->type
== type
&& v
->owner
== owner
) {
1747 this->maxid
= max
<UnitID
>(this->maxid
, v
->unitnumber
);
1751 if (this->maxid
== 0) return;
1753 /* Reserving 'maxid + 2' because we need:
1754 * - space for the last item (with v->unitnumber == maxid)
1755 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1756 this->cache
= CallocT
<bool>(this->maxid
+ 2);
1758 /* Fill the cache */
1759 FOR_ALL_VEHICLES(v
) {
1760 if (v
->type
== type
&& v
->owner
== owner
) {
1761 this->cache
[v
->unitnumber
] = true;
1766 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1767 UnitID
FreeUnitIDGenerator::NextID()
1769 if (this->maxid
<= this->curid
) return ++this->curid
;
1771 while (this->cache
[++this->curid
]) { } // it will stop, we reserved more space than needed
1777 * Get an unused unit number for a vehicle (if allowed).
1778 * @param type Type of vehicle
1779 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1781 UnitID
GetFreeUnitNumber(VehicleType type
)
1783 /* Check whether it is allowed to build another vehicle. */
1786 case VEH_TRAIN
: max_veh
= _settings_game
.vehicle
.max_trains
; break;
1787 case VEH_ROAD
: max_veh
= _settings_game
.vehicle
.max_roadveh
; break;
1788 case VEH_SHIP
: max_veh
= _settings_game
.vehicle
.max_ships
; break;
1789 case VEH_AIRCRAFT
: max_veh
= _settings_game
.vehicle
.max_aircraft
; break;
1790 default: NOT_REACHED();
1793 const Company
*c
= Company::Get(_current_company
);
1794 if (c
->group_all
[type
].num_vehicle
>= max_veh
) return UINT16_MAX
; // Currently already at the limit, no room to make a new one.
1796 FreeUnitIDGenerator
gen(type
, _current_company
);
1798 return gen
.NextID();
1803 * Check whether we can build infrastructure for the given
1804 * vehicle type. This to disable building stations etc. when
1805 * you are not allowed/able to have the vehicle type yet.
1806 * @param type the vehicle type to check this for
1807 * @return true if there is any reason why you may build
1808 * the infrastructure for the given vehicle type
1810 bool CanBuildVehicleInfrastructure(VehicleType type
)
1812 assert(IsCompanyBuildableVehicleType(type
));
1814 if (!Company::IsValidID(_local_company
)) return false;
1815 if (!_settings_client
.gui
.disable_unsuitable_building
) return true;
1819 case VEH_TRAIN
: max
= _settings_game
.vehicle
.max_trains
; break;
1820 case VEH_ROAD
: max
= _settings_game
.vehicle
.max_roadveh
; break;
1821 case VEH_SHIP
: max
= _settings_game
.vehicle
.max_ships
; break;
1822 case VEH_AIRCRAFT
: max
= _settings_game
.vehicle
.max_aircraft
; break;
1823 default: NOT_REACHED();
1826 /* We can build vehicle infrastructure when we may build the vehicle type */
1828 /* Can we actually build the vehicle type? */
1830 FOR_ALL_ENGINES_OF_TYPE(e
, type
) {
1831 if (HasBit(e
->company_avail
, _local_company
)) return true;
1836 /* We should be able to build infrastructure when we have the actual vehicle type */
1838 FOR_ALL_VEHICLES(v
) {
1839 if (v
->owner
== _local_company
&& v
->type
== type
) return true;
1847 * Determines the #LiveryScheme for a vehicle.
1848 * @param engine_type Engine of the vehicle.
1849 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1850 * @param v the vehicle, \c NULL if in purchase list etc.
1851 * @return livery scheme to use.
1853 LiveryScheme
GetEngineLiveryScheme(EngineID engine_type
, EngineID parent_engine_type
, const Vehicle
*v
)
1855 CargoID cargo_type
= v
== NULL
? (CargoID
)CT_INVALID
: v
->cargo_type
;
1856 const Engine
*e
= Engine::Get(engine_type
);
1858 default: NOT_REACHED();
1860 if (v
!= NULL
&& parent_engine_type
!= INVALID_ENGINE
&& (UsesWagonOverride(v
) || (v
->IsArticulatedPart() && e
->u
.rail
.railveh_type
!= RAILVEH_WAGON
))) {
1861 /* Wagonoverrides use the colour scheme of the front engine.
1862 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1863 engine_type
= parent_engine_type
;
1864 e
= Engine::Get(engine_type
);
1865 /* Note: Luckily cargo_type is not needed for engines */
1868 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1869 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1870 if (e
->u
.rail
.railveh_type
== RAILVEH_WAGON
) {
1871 if (!CargoSpec::Get(cargo_type
)->is_freight
) {
1872 if (parent_engine_type
== INVALID_ENGINE
) {
1873 return LS_PASSENGER_WAGON_STEAM
;
1875 switch (RailVehInfo(parent_engine_type
)->engclass
) {
1876 default: NOT_REACHED();
1877 case EC_STEAM
: return LS_PASSENGER_WAGON_STEAM
;
1878 case EC_DIESEL
: return LS_PASSENGER_WAGON_DIESEL
;
1879 case EC_ELECTRIC
: return LS_PASSENGER_WAGON_ELECTRIC
;
1880 case EC_MONORAIL
: return LS_PASSENGER_WAGON_MONORAIL
;
1881 case EC_MAGLEV
: return LS_PASSENGER_WAGON_MAGLEV
;
1885 return LS_FREIGHT_WAGON
;
1888 bool is_mu
= HasBit(e
->info
.misc_flags
, EF_RAIL_IS_MU
);
1890 switch (e
->u
.rail
.engclass
) {
1891 default: NOT_REACHED();
1892 case EC_STEAM
: return LS_STEAM
;
1893 case EC_DIESEL
: return is_mu
? LS_DMU
: LS_DIESEL
;
1894 case EC_ELECTRIC
: return is_mu
? LS_EMU
: LS_ELECTRIC
;
1895 case EC_MONORAIL
: return LS_MONORAIL
;
1896 case EC_MAGLEV
: return LS_MAGLEV
;
1901 /* Always use the livery of the front */
1902 if (v
!= NULL
&& parent_engine_type
!= INVALID_ENGINE
) {
1903 engine_type
= parent_engine_type
;
1904 e
= Engine::Get(engine_type
);
1905 cargo_type
= v
->First()->cargo_type
;
1907 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1908 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1910 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1911 if (HasBit(e
->info
.misc_flags
, EF_ROAD_TRAM
)) {
1913 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_TRAM
: LS_FREIGHT_TRAM
;
1916 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_BUS
: LS_TRUCK
;
1920 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
1921 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
1922 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_SHIP
: LS_FREIGHT_SHIP
;
1925 switch (e
->u
.air
.subtype
) {
1926 case AIR_HELI
: return LS_HELICOPTER
;
1927 case AIR_CTOL
: return LS_SMALL_PLANE
;
1928 case AIR_CTOL
| AIR_FAST
: return LS_LARGE_PLANE
;
1929 default: NOT_REACHED();
1935 * Determines the livery for a vehicle.
1936 * @param engine_type EngineID of the vehicle
1937 * @param company Owner of the vehicle
1938 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1939 * @param v the vehicle. NULL if in purchase list etc.
1940 * @param livery_setting The livery settings to use for acquiring the livery information.
1941 * @return livery to use
1943 const Livery
*GetEngineLivery(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
, byte livery_setting
)
1945 const Company
*c
= Company::Get(company
);
1946 LiveryScheme scheme
= LS_DEFAULT
;
1948 /* The default livery is always available for use, but its in_use flag determines
1949 * whether any _other_ liveries are in use. */
1950 if (c
->livery
[LS_DEFAULT
].in_use
&& (livery_setting
== LIT_ALL
|| (livery_setting
== LIT_COMPANY
&& company
== _local_company
))) {
1951 /* Determine the livery scheme to use */
1952 scheme
= GetEngineLiveryScheme(engine_type
, parent_engine_type
, v
);
1954 /* Switch back to the default scheme if the resolved scheme is not in use */
1955 if (!c
->livery
[scheme
].in_use
) scheme
= LS_DEFAULT
;
1958 return &c
->livery
[scheme
];
1962 static PaletteID
GetEngineColourMap(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
)
1964 PaletteID map
= (v
!= NULL
) ? v
->colourmap
: PAL_NONE
;
1966 /* Return cached value if any */
1967 if (map
!= PAL_NONE
) return map
;
1969 const Engine
*e
= Engine::Get(engine_type
);
1971 /* Check if we should use the colour map callback */
1972 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_COLOUR_REMAP
)) {
1973 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING
, 0, 0, engine_type
, v
);
1974 /* Failure means "use the default two-colour" */
1975 if (callback
!= CALLBACK_FAILED
) {
1976 assert_compile(PAL_NONE
== 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1977 map
= GB(callback
, 0, 14);
1978 /* If bit 14 is set, then the company colours are applied to the
1979 * map else it's returned as-is. */
1980 if (!HasBit(callback
, 14)) {
1982 if (v
!= NULL
) const_cast<Vehicle
*>(v
)->colourmap
= map
;
1988 bool twocc
= HasBit(e
->info
.misc_flags
, EF_USES_2CC
);
1990 if (map
== PAL_NONE
) map
= twocc
? (PaletteID
)SPR_2CCMAP_BASE
: (PaletteID
)PALETTE_RECOLOUR_START
;
1992 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1993 if (!Company::IsValidID(company
)) return map
;
1995 const Livery
*livery
= GetEngineLivery(engine_type
, company
, parent_engine_type
, v
, _settings_client
.gui
.liveries
);
1997 map
+= livery
->colour1
;
1998 if (twocc
) map
+= livery
->colour2
* 16;
2001 if (v
!= NULL
) const_cast<Vehicle
*>(v
)->colourmap
= map
;
2006 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2007 * @param engine_type ID of engine
2008 * @param company ID of company
2009 * @return A ready-to-use palette modifier
2011 PaletteID
GetEnginePalette(EngineID engine_type
, CompanyID company
)
2013 return GetEngineColourMap(engine_type
, company
, INVALID_ENGINE
, NULL
);
2017 * Get the colour map for a vehicle.
2018 * @param v Vehicle to get colour map for
2019 * @return A ready-to-use palette modifier
2021 PaletteID
GetVehiclePalette(const Vehicle
*v
)
2023 if (v
->IsGroundVehicle()) {
2024 return GetEngineColourMap(v
->engine_type
, v
->owner
, v
->GetGroundVehicleCache()->first_engine
, v
);
2027 return GetEngineColourMap(v
->engine_type
, v
->owner
, INVALID_ENGINE
, v
);
2031 * Delete all implicit orders which were not reached.
2033 void Vehicle::DeleteUnreachedImplicitOrders()
2035 if (this->IsGroundVehicle()) {
2036 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2037 if (HasBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
)) {
2038 /* Do not delete orders, only skip them */
2039 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2040 this->cur_implicit_order_index
= this->cur_real_order_index
;
2041 InvalidateVehicleOrder(this, 0);
2046 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2047 while (order
!= NULL
) {
2048 if (this->cur_implicit_order_index
== this->cur_real_order_index
) break;
2050 if (order
->IsType(OT_IMPLICIT
)) {
2051 DeleteOrder(this, this->cur_implicit_order_index
);
2052 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2053 order
= this->GetOrder(this->cur_implicit_order_index
);
2055 /* Skip non-implicit orders, e.g. service-orders */
2056 order
= order
->next
;
2057 this->cur_implicit_order_index
++;
2061 if (order
== NULL
) {
2062 order
= this->GetOrder(0);
2063 this->cur_implicit_order_index
= 0;
2069 * Prepare everything to begin the loading when arriving at a station.
2070 * @pre IsStationTile(this->tile) || this->type == VEH_SHIP.
2072 void Vehicle::BeginLoading()
2074 assert(IsStationTile(this->tile
) || this->type
== VEH_SHIP
);
2076 if (this->current_order
.IsType(OT_GOTO_STATION
) &&
2077 this->current_order
.GetDestination() == this->last_station_visited
) {
2078 this->DeleteUnreachedImplicitOrders();
2080 /* Now both order indices point to the destination station, and we can start loading */
2081 this->current_order
.MakeLoading(true);
2082 UpdateVehicleTimetable(this, true);
2084 /* Furthermore add the Non Stop flag to mark that this station
2085 * is the actual destination of the vehicle, which is (for example)
2086 * necessary to be known for HandleTrainLoading to determine
2087 * whether the train is lost or not; not marking a train lost
2088 * that arrives at random stations is bad. */
2089 this->current_order
.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION
);
2092 /* We weren't scheduled to stop here. Insert an implicit order
2093 * to show that we are stopping here.
2094 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2095 * the 'wrong' terminal when skipping orders etc. */
2096 Order
*in_list
= this->GetOrder(this->cur_implicit_order_index
);
2097 if (this->IsGroundVehicle() &&
2098 (in_list
== NULL
|| !in_list
->IsType(OT_IMPLICIT
) ||
2099 in_list
->GetDestination() != this->last_station_visited
)) {
2100 bool suppress_implicit_orders
= HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS
);
2101 /* Do not create consecutive duplicates of implicit orders */
2102 Order
*prev_order
= this->cur_implicit_order_index
> 0 ? this->GetOrder(this->cur_implicit_order_index
- 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL
);
2103 if (prev_order
== NULL
||
2104 (!prev_order
->IsType(OT_IMPLICIT
) && !prev_order
->IsType(OT_GOTO_STATION
)) ||
2105 prev_order
->GetDestination() != this->last_station_visited
) {
2107 /* Prefer deleting implicit orders instead of inserting new ones,
2108 * so test whether the right order follows later. In case of only
2109 * implicit orders treat the last order in the list like an
2110 * explicit one, except if the overall number of orders surpasses
2111 * IMPLICIT_ORDER_ONLY_CAP. */
2112 int target_index
= this->cur_implicit_order_index
;
2114 while (target_index
!= this->cur_real_order_index
|| this->GetNumManualOrders() == 0) {
2115 const Order
*order
= this->GetOrder(target_index
);
2116 if (order
== NULL
) break; // No orders.
2117 if (order
->IsType(OT_IMPLICIT
) && order
->GetDestination() == this->last_station_visited
) {
2122 if (target_index
>= this->orders
.list
->GetNumOrders()) {
2123 if (this->GetNumManualOrders() == 0 &&
2124 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP
) {
2129 if (target_index
== this->cur_implicit_order_index
) break; // Avoid infinite loop.
2133 if (suppress_implicit_orders
) {
2134 /* Skip to the found order */
2135 this->cur_implicit_order_index
= target_index
;
2136 InvalidateVehicleOrder(this, 0);
2138 /* Delete all implicit orders up to the station we just reached */
2139 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2140 while (!order
->IsType(OT_IMPLICIT
) || order
->GetDestination() != this->last_station_visited
) {
2141 if (order
->IsType(OT_IMPLICIT
)) {
2142 DeleteOrder(this, this->cur_implicit_order_index
);
2143 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2144 order
= this->GetOrder(this->cur_implicit_order_index
);
2146 /* Skip non-implicit orders, e.g. service-orders */
2147 order
= order
->next
;
2148 this->cur_implicit_order_index
++;
2152 if (order
== NULL
) {
2153 order
= this->GetOrder(0);
2154 this->cur_implicit_order_index
= 0;
2156 assert(order
!= NULL
);
2159 } else if (!suppress_implicit_orders
&&
2160 ((this->orders
.list
== NULL
? OrderList::CanAllocateItem() : this->orders
.list
->GetNumOrders() < MAX_VEH_ORDER_ID
)) &&
2161 Order::CanAllocateItem()) {
2162 /* Insert new implicit order */
2163 Order
*implicit_order
= new Order();
2164 implicit_order
->MakeImplicit(this->last_station_visited
);
2165 InsertOrder(this, implicit_order
, this->cur_implicit_order_index
);
2166 if (this->cur_implicit_order_index
> 0) --this->cur_implicit_order_index
;
2168 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2169 * Reenable it for this vehicle */
2170 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2171 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2175 this->current_order
.MakeLoading(false);
2178 if (this->last_loading_station
!= INVALID_STATION
&&
2179 this->last_loading_station
!= this->last_station_visited
&&
2180 ((this->current_order
.GetLoadType() & OLFB_NO_LOAD
) == 0 ||
2181 (this->current_order
.GetUnloadType() & OUFB_NO_UNLOAD
) == 0)) {
2182 IncreaseStats(Station::Get(this->last_loading_station
), this, this->last_station_visited
);
2185 PrepareUnload(this);
2187 SetWindowDirty(GetWindowClassForVehicleType(this->type
), this->owner
);
2188 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2189 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
2190 SetWindowDirty(WC_STATION_VIEW
, this->last_station_visited
);
2192 Station::Get(this->last_station_visited
)->MarkTilesDirty(true);
2193 this->cur_speed
= 0;
2198 * Return all reserved cargo packets to the station and reset all packets
2199 * staged for transfer.
2200 * @param st the station where the reserved packets should go.
2202 void Vehicle::CancelReservation(StationID next
, Station
*st
)
2204 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->next
) {
2205 VehicleCargoList
&cargo
= v
->cargo
;
2206 if (cargo
.ActionCount(VehicleCargoList::MTA_LOAD
) > 0) {
2207 DEBUG(misc
, 1, "cancelling cargo reservation");
2208 cargo
.Return(UINT_MAX
, &st
->goods
[v
->cargo_type
].cargo
, next
);
2209 cargo
.SetTransferLoadPlace(st
->xy
);
2216 * Perform all actions when leaving a station.
2217 * @pre this->current_order.IsType(OT_LOADING)
2219 void Vehicle::LeaveStation()
2221 assert(this->current_order
.IsType(OT_LOADING
));
2223 delete this->cargo_payment
;
2225 /* Only update the timetable if the vehicle was supposed to stop here. */
2226 if (this->current_order
.GetNonStopType() != ONSF_STOP_EVERYWHERE
) UpdateVehicleTimetable(this, false);
2228 if ((this->current_order
.GetLoadType() & OLFB_NO_LOAD
) == 0 ||
2229 (this->current_order
.GetUnloadType() & OUFB_NO_UNLOAD
) == 0) {
2230 if (this->current_order
.CanLeaveWithCargo(this->last_loading_station
!= INVALID_STATION
)) {
2231 /* Refresh next hop stats to make sure we've done that at least once
2232 * during the stop and that refit_cap == cargo_cap for each vehicle in
2234 this->ResetRefitCaps();
2235 LinkRefresher::Run(this);
2237 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2238 this->last_loading_station
= this->last_station_visited
;
2240 /* if the vehicle couldn't load and had to unload or transfer everything
2241 * set the last loading station to invalid as it will leave empty. */
2242 this->last_loading_station
= INVALID_STATION
;
2246 this->current_order
.MakeLeaveStation();
2247 Station
*st
= Station::Get(this->last_station_visited
);
2248 this->CancelReservation(INVALID_STATION
, st
);
2249 st
->loading_vehicles
.remove(this);
2251 HideFillingPercent(&this->fill_percent_te_id
);
2253 if (this->type
== VEH_TRAIN
&& !(this->vehstatus
& VS_CRASHED
)) {
2254 /* Trigger station animation (trains only) */
2255 if (IsStationTile(this->tile
)) {
2256 TriggerStationRandomisation(st
, this->tile
, SRT_TRAIN_DEPARTS
);
2257 TriggerStationAnimation(st
, this->tile
, SAT_TRAIN_DEPARTS
);
2260 SetBit(Train::From(this)->flags
, VRF_LEAVING_STATION
);
2267 * Reset all refit_cap in the consist to cargo_cap.
2269 void Vehicle::ResetRefitCaps()
2271 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) v
->refit_cap
= v
->cargo_cap
;
2275 * Handle the loading of the vehicle; when not it skips through dummy
2276 * orders and does nothing in all other cases.
2277 * @param mode is the non-first call for this vehicle in this tick?
2279 void Vehicle::HandleLoading(bool mode
)
2281 switch (this->current_order
.GetType()) {
2283 uint wait_time
= max(this->current_order
.wait_time
- this->lateness_counter
, 0);
2285 /* Not the first call for this tick, or still loading */
2286 if (mode
|| !HasBit(this->vehicle_flags
, VF_LOADING_FINISHED
) || this->current_order_time
< wait_time
) return;
2288 this->PlayLeaveStationSound();
2290 this->LeaveStation();
2292 /* Only advance to next order if we just loaded at the current one */
2293 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2294 if (order
== NULL
||
2295 (!order
->IsType(OT_IMPLICIT
) && !order
->IsType(OT_GOTO_STATION
)) ||
2296 order
->GetDestination() != this->last_station_visited
) {
2302 case OT_DUMMY
: break;
2307 this->IncrementImplicitOrderIndex();
2311 * Get a map of cargoes and free capacities in the consist.
2312 * @param capacities Map to be filled with cargoes and capacities.
2314 void Vehicle::GetConsistFreeCapacities(SmallMap
<CargoID
, uint
> &capacities
) const
2316 for (const Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) {
2317 if (v
->cargo_cap
== 0) continue;
2318 SmallPair
<CargoID
, uint
> *pair
= capacities
.Find(v
->cargo_type
);
2319 if (pair
== capacities
.End()) {
2320 pair
= capacities
.Append();
2321 pair
->first
= v
->cargo_type
;
2322 pair
->second
= v
->cargo_cap
- v
->cargo
.StoredCount();
2324 pair
->second
+= v
->cargo_cap
- v
->cargo
.StoredCount();
2329 uint
Vehicle::GetConsistTotalCapacity() const
2332 for (const Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) {
2333 result
+= v
->cargo_cap
;
2339 * Send this vehicle to the depot using the given command(s).
2340 * @param flags the command flags (like execute and such).
2341 * @param command the command to execute.
2342 * @return the cost of the depot action.
2344 CommandCost
Vehicle::SendToDepot(DoCommandFlag flags
, DepotCommand command
)
2346 CommandCost ret
= CheckOwnership(this->owner
);
2347 if (ret
.Failed()) return ret
;
2349 if (this->vehstatus
& VS_CRASHED
) return CMD_ERROR
;
2350 if (this->IsStoppedInDepot()) return CMD_ERROR
;
2352 if (this->current_order
.IsType(OT_GOTO_DEPOT
)) {
2353 bool halt_in_depot
= (this->current_order
.GetDepotActionType() & ODATFB_HALT
) != 0;
2354 if (!!(command
& DEPOT_SERVICE
) == halt_in_depot
) {
2355 /* We called with a different DEPOT_SERVICE setting.
2356 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2357 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2358 if (flags
& DC_EXEC
) {
2359 this->current_order
.SetDepotOrderType(ODTF_MANUAL
);
2360 this->current_order
.SetDepotActionType(halt_in_depot
? ODATF_SERVICE_ONLY
: ODATFB_HALT
);
2361 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2363 return CommandCost();
2366 if (command
& DEPOT_DONT_CANCEL
) return CMD_ERROR
; // Requested no cancelation of depot orders
2367 if (flags
& DC_EXEC
) {
2368 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2369 * then skip to the next order; effectively cancelling this forced service */
2370 if (this->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) this->IncrementRealOrderIndex();
2372 if (this->IsGroundVehicle()) {
2373 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2374 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2377 this->current_order
.MakeDummy();
2378 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2380 return CommandCost();
2384 DestinationID destination
;
2386 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
};
2387 if (!this->FindClosestDepot(&location
, &destination
, &reverse
)) return_cmd_error(no_depot
[this->type
]);
2389 if (flags
& DC_EXEC
) {
2390 if (this->current_order
.IsType(OT_LOADING
)) this->LeaveStation();
2392 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2393 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2394 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2397 this->dest_tile
= location
;
2398 this->current_order
.MakeGoToDepot(destination
, ODTF_MANUAL
);
2399 if (!(command
& DEPOT_SERVICE
)) this->current_order
.SetDepotActionType(ODATFB_HALT
);
2400 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2402 /* If there is no depot in front, reverse automatically (trains only) */
2403 if (this->type
== VEH_TRAIN
&& reverse
) DoCommand(this->tile
, this->index
, 0, DC_EXEC
, CMD_REVERSE_TRAIN_DIRECTION
);
2405 if (this->type
== VEH_AIRCRAFT
) {
2406 Aircraft
*a
= Aircraft::From(this);
2407 if (a
->state
== FLYING
&& a
->targetairport
!= destination
) {
2408 /* The aircraft is now heading for a different hangar than the next in the orders */
2409 extern void AircraftNextAirportPos_and_Order(Aircraft
*a
);
2410 AircraftNextAirportPos_and_Order(a
);
2415 return CommandCost();
2420 * Update the cached visual effect.
2421 * @param allow_power_change true if the wagon-is-powered-state may change.
2423 void Vehicle::UpdateVisualEffect(bool allow_power_change
)
2425 bool powered_before
= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2426 const Engine
*e
= this->GetEngine();
2428 /* Evaluate properties */
2431 case VEH_TRAIN
: visual_effect
= e
->u
.rail
.visual_effect
; break;
2432 case VEH_ROAD
: visual_effect
= e
->u
.road
.visual_effect
; break;
2433 case VEH_SHIP
: visual_effect
= e
->u
.ship
.visual_effect
; break;
2434 default: visual_effect
= 1 << VE_DISABLE_EFFECT
; break;
2437 /* Check powered wagon / visual effect callback */
2438 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_VISUAL_EFFECT
)) {
2439 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT
, 0, 0, this->engine_type
, this);
2441 if (callback
!= CALLBACK_FAILED
) {
2442 if (callback
>= 0x100 && e
->GetGRF()->grf_version
>= 8) ErrorUnknownCallbackResult(e
->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT
, callback
);
2444 callback
= GB(callback
, 0, 8);
2445 /* Avoid accidentally setting 'visual_effect' to the default value
2446 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2447 if (callback
== VE_DEFAULT
) {
2448 assert(HasBit(callback
, VE_DISABLE_EFFECT
));
2449 SB(callback
, VE_TYPE_START
, VE_TYPE_COUNT
, 0);
2451 visual_effect
= callback
;
2455 /* Apply default values */
2456 if (visual_effect
== VE_DEFAULT
||
2457 (!HasBit(visual_effect
, VE_DISABLE_EFFECT
) && GB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
) == VE_TYPE_DEFAULT
)) {
2458 /* Only train engines have default effects.
2459 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2460 if (e
->type
!= VEH_TRAIN
|| e
->u
.rail
.railveh_type
== RAILVEH_WAGON
|| !IsInsideMM(e
->u
.rail
.engclass
, EC_STEAM
, EC_MONORAIL
)) {
2461 if (visual_effect
== VE_DEFAULT
) {
2462 visual_effect
= 1 << VE_DISABLE_EFFECT
;
2464 SetBit(visual_effect
, VE_DISABLE_EFFECT
);
2467 if (visual_effect
== VE_DEFAULT
) {
2468 /* Also set the offset */
2469 visual_effect
= (VE_OFFSET_CENTRE
- (e
->u
.rail
.engclass
== EC_STEAM
? 4 : 0)) << VE_OFFSET_START
;
2471 SB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
, e
->u
.rail
.engclass
- EC_STEAM
+ VE_TYPE_STEAM
);
2475 this->vcache
.cached_vis_effect
= visual_effect
;
2477 if (!allow_power_change
&& powered_before
!= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
)) {
2478 ToggleBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2479 ShowNewGrfVehicleError(this->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_POWERED_WAGON
, GBUG_VEH_POWERED_WAGON
, false);
2483 static const int8 _vehicle_smoke_pos
[8] = {
2484 1, 1, 1, 0, -1, -1, -1, 0
2488 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2489 * @pre this->IsPrimaryVehicle()
2491 void Vehicle::ShowVisualEffect() const
2493 assert(this->IsPrimaryVehicle());
2496 /* Do not show any smoke when:
2497 * - vehicle smoke is disabled by the player
2498 * - the vehicle is slowing down or stopped (by the player)
2499 * - the vehicle is moving very slowly
2501 if (_settings_game
.vehicle
.smoke_amount
== 0 ||
2502 this->vehstatus
& (VS_TRAIN_SLOWING
| VS_STOPPED
) ||
2503 this->cur_speed
< 2) {
2507 uint max_speed
= this->vcache
.cached_max_speed
;
2508 if (this->type
== VEH_TRAIN
) {
2509 const Train
*t
= Train::From(this);
2510 /* For trains, do not show any smoke when:
2511 * - the train is reversing
2512 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2514 if (HasBit(t
->flags
, VRF_REVERSING
) ||
2515 (IsRailStationTile(t
->tile
) && t
->IsFrontEngine() && t
->current_order
.ShouldStopAtStation(t
, GetStationIndex(t
->tile
)) &&
2516 t
->cur_speed
>= t
->Train::GetCurrentMaxSpeed())) {
2520 max_speed
= min(max_speed
, t
->gcache
.cached_max_track_speed
);
2521 max_speed
= min(max_speed
, this->current_order
.max_speed
);
2523 if (this->type
== VEH_ROAD
|| this->type
== VEH_SHIP
) max_speed
= min(max_speed
, this->current_order
.max_speed
* 2);
2525 const Vehicle
*v
= this;
2528 int effect_offset
= GB(v
->vcache
.cached_vis_effect
, VE_OFFSET_START
, VE_OFFSET_COUNT
) - VE_OFFSET_CENTRE
;
2529 byte effect_type
= GB(v
->vcache
.cached_vis_effect
, VE_TYPE_START
, VE_TYPE_COUNT
);
2530 bool disable_effect
= HasBit(v
->vcache
.cached_vis_effect
, VE_DISABLE_EFFECT
);
2532 /* Show no smoke when:
2533 * - Smoke has been disabled for this vehicle
2534 * - The vehicle is not visible
2535 * - The vehicle is under a bridge
2536 * - The vehicle is on a depot tile
2537 * - The vehicle is on a tunnel tile
2538 * - The vehicle is a train engine that is currently unpowered */
2539 if (disable_effect
||
2540 v
->vehstatus
& VS_HIDDEN
||
2541 HasBridgeAbove(v
->tile
) ||
2542 IsDepotTile(v
->tile
) ||
2543 IsTunnelTile(v
->tile
) ||
2544 (v
->type
== VEH_TRAIN
&&
2545 !HasPowerOnRail(Train::From(v
)->railtype
, Train::From(v
)->GetTrackRailType()))) {
2549 /* The effect offset is relative to a point 4 units behind the vehicle's
2550 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2551 * correction factor. */
2552 if (v
->type
== VEH_TRAIN
) effect_offset
+= (VEHICLE_LENGTH
- Train::From(v
)->gcache
.cached_veh_length
) / 2;
2554 int x
= _vehicle_smoke_pos
[v
->direction
] * effect_offset
;
2555 int y
= _vehicle_smoke_pos
[(v
->direction
+ 2) % 8] * effect_offset
;
2557 if (v
->type
== VEH_TRAIN
&& HasBit(Train::From(v
)->flags
, VRF_REVERSE_DIRECTION
)) {
2562 switch (effect_type
) {
2564 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2565 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2566 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2568 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2569 if (GB(v
->tick_counter
, 0, ((4 >> _settings_game
.vehicle
.smoke_amount
) + ((this->cur_speed
* 3) / max_speed
))) == 0) {
2570 CreateEffectVehicleRel(v
, x
, y
, 10, EV_STEAM_SMOKE
);
2575 case VE_TYPE_DIESEL
: {
2576 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2577 * when smoke emission stops.
2578 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2579 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2580 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2581 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2582 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2583 * maximum speed no diesel_smoke is emitted.
2585 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2586 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2587 int power_weight_effect
= 0;
2588 if (v
->type
== VEH_TRAIN
) {
2589 power_weight_effect
= (32 >> (Train::From(this)->gcache
.cached_power
>> 10)) - (32 >> (Train::From(this)->gcache
.cached_weight
>> 9));
2591 if (this->cur_speed
< (max_speed
>> (2 >> _settings_game
.vehicle
.smoke_amount
)) &&
2592 Chance16((64 - ((this->cur_speed
<< 5) / max_speed
) + power_weight_effect
), (512 >> _settings_game
.vehicle
.smoke_amount
))) {
2593 CreateEffectVehicleRel(v
, x
, y
, 10, EV_DIESEL_SMOKE
);
2599 case VE_TYPE_ELECTRIC
:
2600 /* Electric train's spark - more often occurs when train is departing (more load)
2601 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2602 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2603 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2605 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2606 if (GB(v
->tick_counter
, 0, 2) == 0 &&
2607 Chance16((6 - ((this->cur_speed
<< 2) / max_speed
)), (360 >> _settings_game
.vehicle
.smoke_amount
))) {
2608 CreateEffectVehicleRel(v
, x
, y
, 10, EV_ELECTRIC_SPARK
);
2616 } while ((v
= v
->Next()) != NULL
);
2618 if (sound
) PlayVehicleSound(this, VSE_VISUAL_EFFECT
);
2622 * Set the next vehicle of this vehicle.
2623 * @param next the next vehicle. NULL removes the next vehicle.
2625 void Vehicle::SetNext(Vehicle
*next
)
2627 assert(this != next
);
2629 if (this->next
!= NULL
) {
2630 /* We had an old next vehicle. Update the first and previous pointers */
2631 for (Vehicle
*v
= this->next
; v
!= NULL
; v
= v
->Next()) {
2632 v
->first
= this->next
;
2634 this->next
->previous
= NULL
;
2639 if (this->next
!= NULL
) {
2640 /* A new next vehicle. Update the first and previous pointers */
2641 if (this->next
->previous
!= NULL
) this->next
->previous
->next
= NULL
;
2642 this->next
->previous
= this;
2643 for (Vehicle
*v
= this->next
; v
!= NULL
; v
= v
->Next()) {
2644 v
->first
= this->first
;
2650 * Adds this vehicle to a shared vehicle chain.
2651 * @param shared_chain a vehicle of the chain with shared vehicles.
2652 * @pre !this->IsOrderListShared()
2654 void Vehicle::AddToShared(Vehicle
*shared_chain
)
2656 assert(this->previous_shared
== NULL
&& this->next_shared
== NULL
);
2658 if (shared_chain
->orders
.list
== NULL
) {
2659 assert(shared_chain
->previous_shared
== NULL
);
2660 assert(shared_chain
->next_shared
== NULL
);
2661 this->orders
.list
= shared_chain
->orders
.list
= new OrderList(NULL
, shared_chain
);
2664 this->next_shared
= shared_chain
->next_shared
;
2665 this->previous_shared
= shared_chain
;
2667 shared_chain
->next_shared
= this;
2669 if (this->next_shared
!= NULL
) this->next_shared
->previous_shared
= this;
2671 shared_chain
->orders
.list
->AddVehicle(this);
2675 * Removes the vehicle from the shared order list.
2677 void Vehicle::RemoveFromShared()
2679 /* Remember if we were first and the old window number before RemoveVehicle()
2680 * as this changes first if needed. */
2681 bool were_first
= (this->FirstShared() == this);
2682 VehicleListIdentifier
vli(VL_SHARED_ORDERS
, this->type
, this->owner
, this->FirstShared()->index
);
2684 this->orders
.list
->RemoveVehicle(this);
2687 /* We are not the first shared one, so only relink our previous one. */
2688 this->previous_shared
->next_shared
= this->NextShared();
2691 if (this->next_shared
!= NULL
) this->next_shared
->previous_shared
= this->previous_shared
;
2694 if (this->orders
.list
->GetNumVehicles() == 1) {
2695 /* When there is only one vehicle, remove the shared order list window. */
2696 DeleteWindowById(GetWindowClassForVehicleType(this->type
), vli
.Pack());
2697 InvalidateVehicleOrder(this->FirstShared(), 0);
2698 } else if (were_first
) {
2699 /* If we were the first one, update to the new first one.
2700 * Note: FirstShared() is already the new first */
2701 InvalidateWindowData(GetWindowClassForVehicleType(this->type
), vli
.Pack(), this->FirstShared()->index
| (1U << 31));
2704 this->next_shared
= NULL
;
2705 this->previous_shared
= NULL
;
2708 void VehiclesYearlyLoop()
2711 FOR_ALL_VEHICLES(v
) {
2712 if (v
->IsPrimaryVehicle()) {
2713 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2714 Money profit
= v
->GetDisplayProfitThisYear();
2715 if (v
->age
>= 730 && profit
< 0) {
2716 if (_settings_client
.gui
.vehicle_income_warn
&& v
->owner
== _local_company
) {
2717 SetDParam(0, v
->index
);
2718 SetDParam(1, profit
);
2719 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE
, v
->index
);
2721 AI::NewEvent(v
->owner
, new ScriptEventVehicleUnprofitable(v
->index
));
2724 v
->profit_last_year
= v
->profit_this_year
;
2725 v
->profit_this_year
= 0;
2726 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
2729 GroupStatistics::UpdateProfits();
2730 SetWindowClassesDirty(WC_TRAINS_LIST
);
2731 SetWindowClassesDirty(WC_SHIPS_LIST
);
2732 SetWindowClassesDirty(WC_ROADVEH_LIST
);
2733 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
2738 * Can this station be used by the given engine type?
2739 * @param engine_type the type of vehicles to test
2740 * @param st the station to test for
2741 * @return true if and only if the vehicle of the type can use this station.
2742 * @note For road vehicles the Vehicle is needed to determine whether it can
2743 * use the station. This function will return true for road vehicles
2744 * when at least one of the facilities is available.
2746 bool CanVehicleUseStation(EngineID engine_type
, const Station
*st
)
2748 const Engine
*e
= Engine::GetIfValid(engine_type
);
2753 return (st
->facilities
& FACIL_TRAIN
) != 0;
2756 /* For road vehicles we need the vehicle to know whether it can actually
2757 * use the station, but if it doesn't have facilities for RVs it is
2758 * certainly not possible that the station can be used. */
2759 return (st
->facilities
& (FACIL_BUS_STOP
| FACIL_TRUCK_STOP
)) != 0;
2762 return (st
->facilities
& FACIL_DOCK
) != 0;
2765 return (st
->facilities
& FACIL_AIRPORT
) != 0 &&
2766 (st
->airport
.GetFTA()->flags
& (e
->u
.air
.subtype
& AIR_CTOL
? AirportFTAClass::AIRPLANES
: AirportFTAClass::HELICOPTERS
)) != 0;
2774 * Can this station be used by the given vehicle?
2775 * @param v the vehicle to test
2776 * @param st the station to test for
2777 * @return true if and only if the vehicle can use this station.
2779 bool CanVehicleUseStation(const Vehicle
*v
, const Station
*st
)
2781 if (v
->type
== VEH_ROAD
) return st
->GetPrimaryRoadStop(RoadVehicle::From(v
)) != NULL
;
2783 return CanVehicleUseStation(v
->engine_type
, st
);
2787 * Access the ground vehicle cache of the vehicle.
2788 * @pre The vehicle is a #GroundVehicle.
2789 * @return #GroundVehicleCache of the vehicle.
2791 GroundVehicleCache
*Vehicle::GetGroundVehicleCache()
2793 assert(this->IsGroundVehicle());
2794 if (this->type
== VEH_TRAIN
) {
2795 return &Train::From(this)->gcache
;
2797 return &RoadVehicle::From(this)->gcache
;
2802 * Access the ground vehicle cache of the vehicle.
2803 * @pre The vehicle is a #GroundVehicle.
2804 * @return #GroundVehicleCache of the vehicle.
2806 const GroundVehicleCache
*Vehicle::GetGroundVehicleCache() const
2808 assert(this->IsGroundVehicle());
2809 if (this->type
== VEH_TRAIN
) {
2810 return &Train::From(this)->gcache
;
2812 return &RoadVehicle::From(this)->gcache
;
2817 * Access the ground vehicle flags of the vehicle.
2818 * @pre The vehicle is a #GroundVehicle.
2819 * @return #GroundVehicleFlags of the vehicle.
2821 uint16
&Vehicle::GetGroundVehicleFlags()
2823 assert(this->IsGroundVehicle());
2824 if (this->type
== VEH_TRAIN
) {
2825 return Train::From(this)->gv_flags
;
2827 return RoadVehicle::From(this)->gv_flags
;
2832 * Access the ground vehicle flags of the vehicle.
2833 * @pre The vehicle is a #GroundVehicle.
2834 * @return #GroundVehicleFlags of the vehicle.
2836 const uint16
&Vehicle::GetGroundVehicleFlags() const
2838 assert(this->IsGroundVehicle());
2839 if (this->type
== VEH_TRAIN
) {
2840 return Train::From(this)->gv_flags
;
2842 return RoadVehicle::From(this)->gv_flags
;
2847 * Calculates the set of vehicles that will be affected by a given selection.
2848 * @param set [inout] Set of affected vehicles.
2849 * @param v First vehicle of the selection.
2850 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2851 * @pre \a set must be empty.
2852 * @post \a set will contain the vehicles that will be refitted.
2854 void GetVehicleSet(VehicleSet
&set
, Vehicle
*v
, uint8 num_vehicles
)
2856 if (v
->type
== VEH_TRAIN
) {
2857 Train
*u
= Train::From(v
);
2858 /* Only include whole vehicles, so start with the first articulated part */
2859 u
= u
->GetFirstEnginePart();
2861 /* Include num_vehicles vehicles, not counting articulated parts */
2862 for (; u
!= NULL
&& num_vehicles
> 0; num_vehicles
--) {
2864 /* Include current vehicle in the selection. */
2865 set
.Include(u
->index
);
2867 /* If the vehicle is multiheaded, add the other part too. */
2868 if (u
->IsMultiheaded()) set
.Include(u
->other_multiheaded_part
->index
);
2871 } while (u
!= NULL
&& u
->IsArticulatedPart());