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 "depot_base.h"
43 #include "core/random_func.hpp"
44 #include "core/backup_type.hpp"
45 #include "order_backup.h"
46 #include "sound_func.h"
47 #include "effectvehicle_func.h"
48 #include "effectvehicle_base.h"
49 #include "vehiclelist.h"
50 #include "bridge_map.h"
51 #include "tunnel_map.h"
52 #include "depot_map.h"
54 #include "linkgraph/linkgraph.h"
55 #include "linkgraph/refresh.h"
56 #include "blitter/factory.hpp"
58 #include "table/strings.h"
62 #include "tbtr_template_vehicle_func.h"
63 #include "safeguards.h"
65 /* Number of bits in the hash to use from each vehicle coord */
66 static const uint GEN_HASHX_BITS
= 6;
67 static const uint GEN_HASHY_BITS
= 6;
69 /* Size of each hash bucket */
70 static const uint GEN_HASHX_BUCKET_BITS
= 7;
71 static const uint GEN_HASHY_BUCKET_BITS
= 6;
73 /* Compute hash for vehicle coord */
74 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
75 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
76 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
78 /* Maximum size until hash repeats */
79 static const int GEN_HASHX_SIZE
= 1 << (GEN_HASHX_BUCKET_BITS
+ GEN_HASHX_BITS
+ ZOOM_LVL_SHIFT
);
80 static const int GEN_HASHY_SIZE
= 1 << (GEN_HASHY_BUCKET_BITS
+ GEN_HASHY_BITS
+ ZOOM_LVL_SHIFT
);
82 /* Increments to reach next bucket in hash table */
83 static const int GEN_HASHX_INC
= 1;
84 static const int GEN_HASHY_INC
= 1 << GEN_HASHX_BITS
;
86 /* Mask to wrap-around buckets */
87 static const uint GEN_HASHX_MASK
= (1 << GEN_HASHX_BITS
) - 1;
88 static const uint GEN_HASHY_MASK
= ((1 << GEN_HASHY_BITS
) - 1) << GEN_HASHX_BITS
;
90 VehicleID _new_vehicle_id
;
91 uint16 _returned_refit_capacity
; ///< Stores the capacity after a refit operation.
92 uint16 _returned_mail_refit_capacity
; ///< Stores the mail capacity after a refit operation (Aircraft only).
95 /** The pool with all our precious vehicles. */
96 VehiclePool
_vehicle_pool("Vehicle");
97 INSTANTIATE_POOL_METHODS(Vehicle
)
101 * Determine shared bounds of all sprites.
102 * @param [out] bounds Shared bounds.
104 void VehicleSpriteSeq::GetBounds(Rect
*bounds
) const
106 bounds
->left
= bounds
->top
= bounds
->right
= bounds
->bottom
= 0;
107 for (uint i
= 0; i
< this->count
; ++i
) {
108 const Sprite
*spr
= GetSprite(this->seq
[i
].sprite
, ST_NORMAL
);
110 bounds
->left
= spr
->x_offs
;
111 bounds
->top
= spr
->y_offs
;
112 bounds
->right
= spr
->width
+ spr
->x_offs
- 1;
113 bounds
->bottom
= spr
->height
+ spr
->y_offs
- 1;
115 if (spr
->x_offs
< bounds
->left
) bounds
->left
= spr
->x_offs
;
116 if (spr
->y_offs
< bounds
->top
) bounds
->top
= spr
->y_offs
;
117 int right
= spr
->width
+ spr
->x_offs
- 1;
118 int bottom
= spr
->height
+ spr
->y_offs
- 1;
119 if (right
> bounds
->right
) bounds
->right
= right
;
120 if (bottom
> bounds
->bottom
) bounds
->bottom
= bottom
;
126 * Draw the sprite sequence.
127 * @param x X position
128 * @param y Y position
129 * @param default_pal Vehicle palette
130 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
132 void VehicleSpriteSeq::Draw(int x
, int y
, PaletteID default_pal
, bool force_pal
) const
134 for (uint i
= 0; i
< this->count
; ++i
) {
135 PaletteID pal
= force_pal
|| !this->seq
[i
].pal
? default_pal
: this->seq
[i
].pal
;
136 DrawSprite(this->seq
[i
].sprite
, pal
, x
, y
);
141 * Function to tell if a vehicle needs to be autorenewed
142 * @param *c The vehicle owner
143 * @param use_renew_setting Should the company renew setting be considered?
144 * @return true if the vehicle is old enough for replacement
146 bool Vehicle::NeedsAutorenewing(const Company
*c
, bool use_renew_setting
) const
148 /* We can always generate the Company pointer when we have the vehicle.
149 * However this takes time and since the Company pointer is often present
150 * when this function is called then it's faster to pass the pointer as an
151 * argument rather than finding it again. */
152 assert(c
== Company::Get(this->owner
));
154 if (use_renew_setting
&& !c
->settings
.engine_renew
) return false;
155 if (this->age
- this->max_age
< (c
->settings
.engine_renew_months
* 30)) return false;
157 /* Only engines need renewing */
158 if (this->type
== VEH_TRAIN
&& !Train::From(this)->IsEngine()) return false;
164 * Service a vehicle and all subsequent vehicles in the consist
166 * @param *v The vehicle or vehicle chain being serviced
168 void VehicleServiceInDepot(Vehicle
*v
)
171 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
); // ensure that last service date and reliability are updated
174 v
->date_of_last_service
= _date
;
175 v
->breakdowns_since_last_service
= 0;
176 v
->reliability
= v
->GetEngine()->reliability
;
177 /* Prevent vehicles from breaking down directly after exiting the depot. */
178 v
->breakdown_chance
= 0;
179 v
->breakdown_ctr
= 0;
181 } while (v
!= NULL
&& v
->HasEngineType());
185 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
187 * @see NeedsAutomaticServicing()
188 * @return true if the vehicle should go to a depot if a opportunity presents itself.
190 bool Vehicle::NeedsServicing() const
192 /* Stopped or crashed vehicles will not move, as such making unmovable
193 * vehicles to go for service is lame. */
194 if (this->vehstatus
& (VS_STOPPED
| VS_CRASHED
)) return false;
196 /* Are we ready for the next service cycle? */
197 const Company
*c
= Company::Get(this->owner
);
199 if (this->ServiceIntervalIsPercent() ?
200 (this->reliability
>= this->GetEngine()->reliability
* (100 - this->GetServiceInterval()) / 100) :
201 (this->date_of_last_service
+ this->GetServiceInterval() >= _date
)) {
205 /* Do we even have any depots/hangars */
207 uint road_pieces
= 0;
209 for (uint i
= 0; i
< lengthof(c
->infrastructure
.rail
); i
++) rail_pices
+= c
->infrastructure
.rail
[i
];
210 for (uint i
= 0; i
< lengthof(c
->infrastructure
.road
); i
++) road_pieces
+= c
->infrastructure
.road
[i
];
212 if ((this->type
== VEH_TRAIN
&& rail_pices
== 0) ||
213 (this->type
== VEH_ROAD
&& road_pieces
== 0) ||
214 (this->type
== VEH_SHIP
&& c
->infrastructure
.water
== 0) ||
215 (this->type
== VEH_AIRCRAFT
&& c
->infrastructure
.airport
== 0)) {
219 /* If we're servicing anyway, because we have not disabled servicing when
220 * there are no breakdowns or we are playing with breakdowns, bail out. */
221 if (!_settings_game
.order
.no_servicing_if_no_breakdowns
||
222 _settings_game
.difficulty
.vehicle_breakdowns
!= 0) {
226 /* Is vehicle old and renewing is enabled */
227 if (this->NeedsAutorenewing(c
, true)) {
231 /* Test whether there is some pending autoreplace.
232 * Note: We do this after the service-interval test.
233 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
234 bool pending_replace
= false;
235 Money needed_money
= c
->settings
.engine_renew_money
;
236 if (needed_money
> c
->money
) return false;
238 for (const Vehicle
*v
= this; v
!= NULL
; v
= (v
->type
== VEH_TRAIN
) ? Train::From(v
)->GetNextUnit() : NULL
) {
239 bool replace_when_old
= false;
240 EngineID new_engine
= EngineReplacementForCompany(c
, v
->engine_type
, v
->group_id
, &replace_when_old
);
242 /* Check engine availability */
243 if (new_engine
== INVALID_ENGINE
|| !HasBit(Engine::Get(new_engine
)->company_avail
, v
->owner
)) continue;
244 /* Is the vehicle old if we are not always replacing? */
245 if (replace_when_old
&& !v
->NeedsAutorenewing(c
, false)) continue;
247 /* Check refittability */
248 uint32 available_cargo_types
, union_mask
;
249 GetArticulatedRefitMasks(new_engine
, true, &union_mask
, &available_cargo_types
);
250 /* Is there anything to refit? */
251 if (union_mask
!= 0) {
253 /* We cannot refit to mixed cargoes in an automated way */
254 if (IsArticulatedVehicleCarryingDifferentCargoes(v
, &cargo_type
)) continue;
256 /* Did the old vehicle carry anything? */
257 if (cargo_type
!= CT_INVALID
) {
258 /* We can't refit the vehicle to carry the cargo we want */
259 if (!HasBit(available_cargo_types
, cargo_type
)) continue;
264 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
265 pending_replace
= true;
266 needed_money
+= 2 * Engine::Get(new_engine
)->GetCost();
267 if (needed_money
> c
->money
) return false;
270 return pending_replace
;
274 * Checks if the current order should be interrupted for a service-in-depot order.
275 * @see NeedsServicing()
276 * @return true if the current order should be interrupted.
278 bool Vehicle::NeedsAutomaticServicing() const
280 if (this->HasDepotOrder()) return false;
281 if (this->current_order
.IsType(OT_LOADING
)) return false;
282 if (this->current_order
.IsType(OT_GOTO_DEPOT
) && this->current_order
.GetDepotOrderType() != ODTFB_SERVICE
) return false;
284 return NeedsServicing();
287 uint
Vehicle::Crash(bool flooded
)
289 assert((this->vehstatus
& VS_CRASHED
) == 0);
290 assert(this->Previous() == NULL
); // IsPrimaryVehicle fails for free-wagon-chains
293 /* Stop the vehicle. */
294 if (this->IsPrimaryVehicle())
295 this->vehstatus
|= VS_STOPPED
;
296 /* crash all wagons, and count passengers */
297 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) {
298 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
299 if (IsCargoInClass(v
->cargo_type
, CC_PASSENGERS
)) pass
+= v
->cargo
.TotalCount();
300 v
->vehstatus
|= VS_CRASHED
;
301 v
->MarkAllViewportsDirty();
304 /* Dirty some windows */
305 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
306 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
307 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
308 SetWindowDirty(WC_VEHICLE_DEPOT
, this->tile
);
310 delete this->cargo_payment
;
311 assert(this->cargo_payment
== NULL
); // cleared by ~CargoPayment
313 return RandomRange(pass
+ 1); // Randomise deceased passengers.
316 /** Marks the separation of this vehicle's order list invalid. */
317 void Vehicle::MarkSeparationInvalid()
319 if (this->orders
.list
!= NULL
) this->orders
.list
->MarkSeparationInvalid();
322 /** Sets new separation settings for this vehicle's shared orders. */
323 void Vehicle::SetSepSettings(TTSepMode Mode
, uint Parameter
)
325 if (this->orders
.list
!= NULL
) this->orders
.list
->SetSepSettings(Mode
, Parameter
);
329 * Get whether a the vehicle should be drawn (i.e. if it isn't hidden, or it is in a tunnel but being shown transparently)
330 * @return whether to show vehicle
332 bool Vehicle::IsDrawn() const
334 return !(HasBit(this->subtype
, GVSF_VIRTUAL
)) && (!(this->vehstatus
& VS_HIDDEN
) ||
335 (IsTransparencySet(TO_TUNNELS
) &&
336 ((this->type
== VEH_TRAIN
&& Train::From(this)->track
== TRACK_BIT_WORMHOLE
) ||
337 (this->type
== VEH_ROAD
&& RoadVehicle::From(this)->state
== RVSB_WORMHOLE
))));
341 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
342 * @param engine The engine that caused the problem
343 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
344 * @param part2 Part 2 of the error message, taking the engine as parameter 2
345 * @param bug_type Flag to check and set in grfconfig
346 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
348 void ShowNewGrfVehicleError(EngineID engine
, StringID part1
, StringID part2
, GRFBugs bug_type
, bool critical
)
350 const Engine
*e
= Engine::Get(engine
);
351 GRFConfig
*grfconfig
= GetGRFConfig(e
->GetGRFID());
353 /* Missing GRF. Nothing useful can be done in this situation. */
354 if (grfconfig
== NULL
) return;
356 if (!HasBit(grfconfig
->grf_bugs
, bug_type
)) {
357 SetBit(grfconfig
->grf_bugs
, bug_type
);
358 SetDParamStr(0, grfconfig
->GetName());
359 SetDParam(1, engine
);
360 ShowErrorMessage(part1
, part2
, WL_CRITICAL
);
361 if (!_networking
) DoCommand(0, critical
? PM_PAUSED_ERROR
: PM_PAUSED_NORMAL
, 1, DC_EXEC
, CMD_PAUSE
);
367 SetDParamStr(0, grfconfig
->GetName());
368 GetString(buffer
, part1
, lastof(buffer
));
369 DEBUG(grf
, 0, "%s", buffer
+ 3);
371 SetDParam(1, engine
);
372 GetString(buffer
, part2
, lastof(buffer
));
373 DEBUG(grf
, 0, "%s", buffer
+ 3);
377 * Logs a bug in GRF and shows a warning message if this
378 * is for the first time this happened.
379 * @param u first vehicle of chain
381 void VehicleLengthChanged(const Vehicle
*u
)
383 /* show a warning once for each engine in whole game and once for each GRF after each game load */
384 const Engine
*engine
= u
->GetEngine();
385 uint32 grfid
= engine
->grf_prop
.grffile
->grfid
;
386 GRFConfig
*grfconfig
= GetGRFConfig(grfid
);
387 if (GamelogGRFBugReverse(grfid
, engine
->grf_prop
.local_id
) || !HasBit(grfconfig
->grf_bugs
, GBUG_VEH_LENGTH
)) {
388 ShowNewGrfVehicleError(u
->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_VEHICLE_LENGTH
, GBUG_VEH_LENGTH
, true);
393 * Vehicle constructor.
394 * @param type Type of the new vehicle.
396 Vehicle::Vehicle(VehicleType type
)
399 this->coord
.left
= INVALID_COORD
;
400 this->group_id
= DEFAULT_GROUP
;
401 this->fill_percent_te_id
= INVALID_TE_ID
;
403 this->colourmap
= PAL_NONE
;
404 this->cargo_age_counter
= 1;
405 this->last_station_visited
= INVALID_STATION
;
406 this->last_loading_station
= INVALID_STATION
;
410 * Get a value for a vehicle's random_bits.
411 * @return A random value from 0 to 255.
413 byte
VehicleRandomBits()
415 return GB(Random(), 0, 8);
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. */
420 const int HASH_BITS
= 7;
421 const int HASH_SIZE
= 1 << HASH_BITS
;
422 const int HASH_MASK
= HASH_SIZE
- 1;
423 const int TOTAL_HASH_SIZE
= 1 << (HASH_BITS
* 2);
424 const int TOTAL_HASH_MASK
= TOTAL_HASH_SIZE
- 1;
426 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
427 * Profiling results show that 0 is fastest. */
428 const int HASH_RES
= 0;
430 static Vehicle
*_vehicle_tile_hash
[TOTAL_HASH_SIZE
];
432 static Vehicle
*VehicleFromTileHash(int xl
, int yl
, int xu
, int yu
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
434 for (int y
= yl
; ; y
= (y
+ (1 << HASH_BITS
)) & (HASH_MASK
<< HASH_BITS
)) {
435 for (int x
= xl
; ; x
= (x
+ 1) & HASH_MASK
) {
436 Vehicle
*v
= _vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
437 for (; v
!= NULL
; v
= v
->hash_tile_next
) {
438 Vehicle
*a
= proc(v
, data
);
439 if (find_first
&& a
!= NULL
) return a
;
451 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
452 * @note Do not call this function directly!
453 * @param x The X location on the map
454 * @param y The Y location on the map
455 * @param data Arbitrary data passed to proc
456 * @param proc The proc that determines whether a vehicle will be "found".
457 * @param find_first Whether to return on the first found or iterate over
459 * @return the best matching or first vehicle (depending on find_first).
461 static Vehicle
*VehicleFromPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
463 const int COLL_DIST
= 6;
465 /* Hash area to scan is from xl,yl to xu,yu */
466 int xl
= GB((x
- COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
);
467 int xu
= GB((x
+ COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
);
468 int yl
= GB((y
- COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
) << HASH_BITS
;
469 int yu
= GB((y
+ COLL_DIST
) / TILE_SIZE
, HASH_RES
, HASH_BITS
) << HASH_BITS
;
471 return VehicleFromTileHash(xl
, yl
, xu
, yu
, data
, proc
, find_first
);
475 * Find a vehicle from a specific location. It will call proc for ALL vehicles
476 * on the tile and YOU must make SURE that the "best one" is stored in the
477 * data value and is ALWAYS the same regardless of the order of the vehicles
478 * where proc was called on!
479 * When you fail to do this properly you create an almost untraceable DESYNC!
480 * @note The return value of proc will be ignored.
481 * @note Use this when you have the intention that all vehicles
482 * should be iterated over.
483 * @param x The X location on the map
484 * @param y The Y location on the map
485 * @param data Arbitrary data passed to proc
486 * @param proc The proc that determines whether a vehicle will be "found".
488 void FindVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
490 VehicleFromPosXY(x
, y
, data
, proc
, false);
494 * Checks whether a vehicle in on a specific location. It will call proc for
495 * vehicles until it returns non-NULL.
496 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
497 * should be iterated over.
498 * @param x The X location on the map
499 * @param y The Y location on the map
500 * @param data Arbitrary data passed to proc
501 * @param proc The proc that determines whether a vehicle will be "found".
502 * @return True if proc returned non-NULL.
504 bool HasVehicleOnPosXY(int x
, int y
, void *data
, VehicleFromPosProc
*proc
)
506 return VehicleFromPosXY(x
, y
, data
, proc
, true) != NULL
;
510 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
511 * @note Do not call this function directly!
512 * @param tile The location on the map
513 * @param data Arbitrary data passed to \a proc.
514 * @param proc The proc that determines whether a vehicle will be "found".
515 * @param find_first Whether to return on the first found or iterate over
517 * @return the best matching or first vehicle (depending on find_first).
519 static Vehicle
*VehicleFromPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
, bool find_first
)
521 int x
= GB(TileX(tile
), HASH_RES
, HASH_BITS
);
522 int y
= GB(TileY(tile
), HASH_RES
, HASH_BITS
) << HASH_BITS
;
524 Vehicle
*v
= _vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
525 for (; v
!= NULL
; v
= v
->hash_tile_next
) {
526 if (v
->tile
!= tile
) continue;
528 Vehicle
*a
= proc(v
, data
);
529 if (find_first
&& a
!= NULL
) return a
;
536 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
537 * on the tile and YOU must make SURE that the "best one" is stored in the
538 * data value and is ALWAYS the same regardless of the order of the vehicles
539 * where proc was called on!
540 * When you fail to do this properly you create an almost untraceable DESYNC!
541 * @note The return value of \a proc will be ignored.
542 * @note Use this function when you have the intention that all vehicles
543 * should be iterated over.
544 * @param tile The location on the map
545 * @param data Arbitrary data passed to \a proc.
546 * @param proc The proc that determines whether a vehicle will be "found".
548 void FindVehicleOnPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
)
550 VehicleFromPos(tile
, data
, proc
, false);
554 * Checks whether a vehicle is on a specific location. It will call \a proc for
555 * vehicles until it returns non-NULL.
556 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
557 * should be iterated over.
558 * @param tile The location on the map
559 * @param data Arbitrary data passed to \a proc.
560 * @param proc The \a proc that determines whether a vehicle will be "found".
561 * @return True if proc returned non-NULL.
563 bool HasVehicleOnPos(TileIndex tile
, void *data
, VehicleFromPosProc
*proc
)
565 return VehicleFromPos(tile
, data
, proc
, true) != NULL
;
569 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
570 * @param v Vehicle to examine.
571 * @param data Pointer to height data.
572 * @return \a v if conditions are met, else \c NULL.
574 static Vehicle
*EnsureNoVehicleProcZ(Vehicle
*v
, void *data
)
578 if (v
->type
== VEH_DISASTER
|| (v
->type
== VEH_AIRCRAFT
&& v
->subtype
== AIR_SHADOW
)) return NULL
;
579 if (v
->z_pos
> z
) return NULL
;
585 * Ensure there is no vehicle at the ground at the given position.
586 * @param tile Position to examine.
587 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
589 CommandCost
EnsureNoVehicleOnGround(TileIndex tile
)
591 int z
= GetTileMaxPixelZ(tile
);
593 /* Value v is not safe in MP games, however, it is used to generate a local
594 * error message only (which may be different for different machines).
595 * Such a message does not affect MP synchronisation.
597 Vehicle
*v
= VehicleFromPos(tile
, &z
, &EnsureNoVehicleProcZ
, true);
598 if (v
!= NULL
) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
599 return CommandCost();
602 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
603 static Vehicle
*GetVehicleTunnelBridgeProc(Vehicle
*v
, void *data
)
605 if (v
->type
!= VEH_TRAIN
&& v
->type
!= VEH_ROAD
&& v
->type
!= VEH_SHIP
) return NULL
;
606 if (v
== (const Vehicle
*)data
) return NULL
;
612 * Finds vehicle in tunnel / bridge
613 * @param tile first end
614 * @param endtile second end
615 * @param ignore Ignore this vehicle when searching
616 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
618 CommandCost
TunnelBridgeIsFree(TileIndex tile
, TileIndex endtile
, const Vehicle
*ignore
)
620 /* Value v is not safe in MP games, however, it is used to generate a local
621 * error message only (which may be different for different machines).
622 * Such a message does not affect MP synchronisation.
624 Vehicle
*v
= VehicleFromPos(tile
, const_cast<Vehicle
*>(ignore
), &GetVehicleTunnelBridgeProc
, true);
625 if (v
== NULL
) v
= VehicleFromPos(endtile
, const_cast<Vehicle
*>(ignore
), &GetVehicleTunnelBridgeProc
, true);
627 if (v
!= NULL
) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
628 return CommandCost();
631 static Vehicle
*EnsureNoTrainOnTrackProc(Vehicle
*v
, void *data
)
633 TrackBits rail_bits
= *(TrackBits
*)data
;
635 if (v
->type
!= VEH_TRAIN
) return NULL
;
637 Train
*t
= Train::From(v
);
638 if ((t
->track
!= rail_bits
) && !TracksOverlap(t
->track
| rail_bits
)) return NULL
;
644 * Tests if a vehicle interacts with the specified track bits.
645 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
647 * @param tile The tile.
648 * @param track_bits The track bits.
649 * @return \c true if no train that interacts, is found. \c false if a train is found.
651 CommandCost
EnsureNoTrainOnTrackBits(TileIndex tile
, TrackBits track_bits
)
653 /* Value v is not safe in MP games, however, it is used to generate a local
654 * error message only (which may be different for different machines).
655 * Such a message does not affect MP synchronisation.
657 Vehicle
*v
= VehicleFromPos(tile
, &track_bits
, &EnsureNoTrainOnTrackProc
, true);
658 if (v
!= NULL
) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY
+ v
->type
);
659 return CommandCost();
662 static void UpdateVehicleTileHash(Vehicle
*v
, bool remove
)
664 Vehicle
**old_hash
= v
->hash_tile_current
;
670 int x
= GB(TileX(v
->tile
), HASH_RES
, HASH_BITS
);
671 int y
= GB(TileY(v
->tile
), HASH_RES
, HASH_BITS
) << HASH_BITS
;
672 new_hash
= &_vehicle_tile_hash
[(x
+ y
) & TOTAL_HASH_MASK
];
675 if (old_hash
== new_hash
) return;
677 /* Remove from the old position in the hash table */
678 if (old_hash
!= NULL
) {
679 if (v
->hash_tile_next
!= NULL
) v
->hash_tile_next
->hash_tile_prev
= v
->hash_tile_prev
;
680 *v
->hash_tile_prev
= v
->hash_tile_next
;
683 /* Insert vehicle at beginning of the new position in the hash table */
684 if (new_hash
!= NULL
) {
685 v
->hash_tile_next
= *new_hash
;
686 if (v
->hash_tile_next
!= NULL
) v
->hash_tile_next
->hash_tile_prev
= &v
->hash_tile_next
;
687 v
->hash_tile_prev
= new_hash
;
691 /* Remember current hash position */
692 v
->hash_tile_current
= new_hash
;
695 static Vehicle
*_vehicle_viewport_hash
[1 << (GEN_HASHX_BITS
+ GEN_HASHY_BITS
)];
697 static void UpdateVehicleViewportHash(Vehicle
*v
, int x
, int y
)
699 Vehicle
**old_hash
, **new_hash
;
700 int old_x
= v
->coord
.left
;
701 int old_y
= v
->coord
.top
;
703 new_hash
= (x
== INVALID_COORD
) ? NULL
: &_vehicle_viewport_hash
[GEN_HASH(x
, y
)];
704 old_hash
= (old_x
== INVALID_COORD
) ? NULL
: &_vehicle_viewport_hash
[GEN_HASH(old_x
, old_y
)];
706 if (old_hash
== new_hash
) return;
708 /* remove from hash table? */
709 if (old_hash
!= NULL
) {
710 if (v
->hash_viewport_next
!= NULL
) v
->hash_viewport_next
->hash_viewport_prev
= v
->hash_viewport_prev
;
711 *v
->hash_viewport_prev
= v
->hash_viewport_next
;
714 /* insert into hash table? */
715 if (new_hash
!= NULL
) {
716 v
->hash_viewport_next
= *new_hash
;
717 if (v
->hash_viewport_next
!= NULL
) v
->hash_viewport_next
->hash_viewport_prev
= &v
->hash_viewport_next
;
718 v
->hash_viewport_prev
= new_hash
;
723 void ResetVehicleHash()
726 FOR_ALL_VEHICLES(v
) { v
->hash_tile_current
= NULL
; }
727 memset(_vehicle_viewport_hash
, 0, sizeof(_vehicle_viewport_hash
));
728 memset(_vehicle_tile_hash
, 0, sizeof(_vehicle_tile_hash
));
731 void ResetVehicleColourMap()
734 FOR_ALL_VEHICLES(v
) { v
->colourmap
= PAL_NONE
; }
738 * List of vehicles that should check for autoreplace this tick.
739 * Mapping of vehicle -> leave depot immediately after autoreplace.
741 typedef SmallMap
<Vehicle
*, bool, 4> AutoreplaceMap
;
742 static AutoreplaceMap _vehicles_to_autoreplace
;
745 * List of vehicles that are issued for template replacement this tick.
746 * Mapping is {vehicle : leave depot after replacement}
748 typedef SmallMap
<Train
*, bool, 4> TemplateReplacementMap
;
749 static TemplateReplacementMap _vehicles_to_templatereplace
;
751 void InitializeVehicles()
753 _vehicles_to_autoreplace
.Reset();
757 uint
CountVehiclesInChain(const Vehicle
*v
)
760 do count
++; while ((v
= v
->Next()) != NULL
);
765 * Check if a vehicle is counted in num_engines in each company struct
766 * @return true if the vehicle is counted in num_engines
768 bool Vehicle::IsEngineCountable() const
770 if (HasBit(this->subtype
, GVSF_VIRTUAL
)) return false;
771 switch (this->type
) {
772 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
774 return !this->IsArticulatedPart() && // tenders and other articulated parts
775 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
776 case VEH_ROAD
: return RoadVehicle::From(this)->IsFrontEngine();
777 case VEH_SHIP
: return true;
778 default: return false; // Only count company buildable vehicles
783 * Check whether Vehicle::engine_type has any meaning.
784 * @return true if the vehicle has a useable engine type.
786 bool Vehicle::HasEngineType() const
788 switch (this->type
) {
789 case VEH_AIRCRAFT
: return Aircraft::From(this)->IsNormalAircraft();
792 case VEH_SHIP
: return true;
793 default: return false;
798 * Retrieves the engine of the vehicle.
799 * @return Engine of the vehicle.
800 * @pre HasEngineType() == true
802 const Engine
*Vehicle::GetEngine() const
804 return Engine::Get(this->engine_type
);
808 * Retrieve the NewGRF the vehicle is tied to.
809 * This is the GRF providing the Action 3 for the engine type.
810 * @return NewGRF associated to the vehicle.
812 const GRFFile
*Vehicle::GetGRF() const
814 return this->GetEngine()->GetGRF();
818 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
819 * This is the GRF providing the Action 3 for the engine type.
820 * @return GRF ID of the associated NewGRF.
822 uint32
Vehicle::GetGRFID() const
824 return this->GetEngine()->GetGRFID();
828 * Handle the pathfinding result, especially the lost status.
829 * If the vehicle is now lost and wasn't previously fire an
830 * event to the AIs and a news message to the user. If the
831 * vehicle is not lost anymore remove the news message.
832 * @param path_found Whether the vehicle has a path to its destination.
834 void Vehicle::HandlePathfindingResult(bool path_found
)
837 /* Route found, is the vehicle marked with "lost" flag? */
838 if (!HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
840 /* Clear the flag as the PF's problem was solved. */
841 ClrBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
842 /* Delete the news item. */
843 DeleteVehicleNews(this->index
, STR_NEWS_VEHICLE_IS_LOST
);
847 /* Were we already lost? */
848 if (HasBit(this->vehicle_flags
, VF_PATHFINDER_LOST
)) return;
850 /* It is first time the problem occurred, set the "lost" flag. */
851 SetBit(this->vehicle_flags
, VF_PATHFINDER_LOST
);
852 /* Notify user about the event. */
853 AI::NewEvent(this->owner
, new ScriptEventVehicleLost(this->index
));
854 if (_settings_client
.gui
.lost_vehicle_warn
&& this->owner
== _local_company
) {
855 SetDParam(0, this->index
);
856 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST
, this->index
);
860 /** Destroy all stuff that (still) needs the virtual functions to work properly */
861 void Vehicle::PreDestructor()
863 if (CleaningPool()) return;
865 if (Station::IsValidID(this->last_station_visited
)) {
866 Station
*st
= Station::Get(this->last_station_visited
);
867 st
->loading_vehicles
.erase(std::remove(st
->loading_vehicles
.begin(), st
->loading_vehicles
.end(), this), st
->loading_vehicles
.end());
869 HideFillingPercent(&this->fill_percent_te_id
);
870 this->CancelReservation(INVALID_STATION
, st
);
871 delete this->cargo_payment
;
872 assert(this->cargo_payment
== NULL
); // cleared by ~CargoPayment
875 if (this->IsEngineCountable()) {
876 GroupStatistics::CountEngine(this, -1);
877 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
878 GroupStatistics::UpdateAutoreplace(this->owner
);
880 if (this->owner
== _local_company
) InvalidateAutoreplaceWindow(this->engine_type
, this->group_id
);
881 DeleteGroupHighlightOfVehicle(this);
884 if (this->type
== VEH_AIRCRAFT
&& this->IsPrimaryVehicle()) {
885 Aircraft
*a
= Aircraft::From(this);
886 Station
*st
= GetTargetAirportIfValid(a
);
888 const AirportFTA
*layout
= st
->airport
.GetFTA()->layout
;
889 CLRBITS(st
->airport
.flags
, layout
[a
->previous_pos
].block
| layout
[a
->pos
].block
);
894 if (this->type
== VEH_ROAD
&& this->IsPrimaryVehicle()) {
895 RoadVehicle
*v
= RoadVehicle::From(this);
896 if (!(v
->vehstatus
& VS_CRASHED
) && IsInsideMM(v
->state
, RVSB_IN_DT_ROAD_STOP
, RVSB_IN_DT_ROAD_STOP_END
)) {
897 /* Leave the drive through roadstop, when you have not already left it. */
898 RoadStop::GetByTile(v
->tile
, GetRoadStopType(v
->tile
))->Leave(v
);
902 if (this->Previous() == NULL
) {
903 InvalidateWindowData(WC_VEHICLE_DEPOT
, this->tile
);
906 if (this->IsPrimaryVehicle()) {
907 DeleteWindowById(WC_VEHICLE_VIEW
, this->index
);
908 DeleteWindowById(WC_VEHICLE_ORDERS
, this->index
);
909 DeleteWindowById(WC_VEHICLE_REFIT
, this->index
);
910 DeleteWindowById(WC_VEHICLE_DETAILS
, this->index
);
911 DeleteWindowById(WC_VEHICLE_TIMETABLE
, this->index
);
912 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_LOAD_ORDERS
, this->index
);
913 DeleteWindowById(WC_VEHICLE_CARGO_TYPE_UNLOAD_ORDERS
, this->index
);
914 SetWindowDirty(WC_COMPANY
, this->owner
);
915 OrderBackup::ClearVehicle(this);
917 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type
), 0);
919 this->cargo
.Truncate();
920 DeleteVehicleOrders(this);
921 DeleteDepotHighlightOfVehicle(this);
923 extern void StopGlobalFollowVehicle(const Vehicle
*v
);
924 StopGlobalFollowVehicle(this);
926 ReleaseDisastersTargetingVehicle(this->index
);
931 if (CleaningPool()) {
932 this->cargo
.OnCleanPool();
936 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
937 * it may happen that vehicle chain is deleted when visible */
938 if (this->IsDrawn()) this->MarkAllViewportsDirty();
940 Vehicle
*v
= this->Next();
945 UpdateVehicleTileHash(this, true);
946 UpdateVehicleViewportHash(this, INVALID_COORD
, 0);
947 DeleteVehicleNews(this->index
, INVALID_STRING_ID
);
948 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type
), this->index
);
952 * Adds a vehicle to the list of vehicles that visited a depot this tick
953 * @param *v vehicle to add
955 void VehicleEnteredDepotThisTick(Vehicle
*v
)
957 /* Template Replacement Setup stuff */
958 bool stayInDepot
= v
->current_order
.GetDepotActionType() != ODATF_SERVICE_ONLY
;
959 TemplateReplacement
*tr
= GetTemplateReplacementByGroupID(v
->group_id
);
961 if ( stayInDepot
) _vehicles_to_templatereplace
[(Train
*)v
] = true;
962 else _vehicles_to_templatereplace
[(Train
*)v
] = false;
964 /* Moved the assignment for auto replacement here to prevent auto replacement
965 * from happening if template replacement is also scheduled */
967 /* Vehicle should stop in the depot if it was in 'stopping' state */
968 _vehicles_to_autoreplace
[v
] = !(v
->vehstatus
& VS_STOPPED
);
970 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
971 * stopping in the depot, so we stop it to ensure that it will not reserve
972 * the path out of the depot before we might autoreplace it to a different
973 * engine. The new engine would not own the reserved path we store that we
974 * stopped the vehicle, so autoreplace can start it again */
977 if (v
->orders
.list
!= NULL
&& (HasBit(v
->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
) || HasBit(v
->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
) ||
978 v
->current_order
.GetDepotOrderType() == ODTF_MANUAL
)) {
979 for (int i
= 0; i
< v
->orders
.list
->GetNumOrders(); ++i
) {
980 Order
* order
= v
->orders
.list
->GetOrderAt(i
);
982 if (order
->GetType() == OT_GOTO_DEPOT
&& order
->GetDestination() == v
->current_order
.GetDestination()) {
983 v
->cur_implicit_order_index
= i
;
984 v
->cur_real_order_index
= i
;
990 ClrBit(v
->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
);
991 ClrBit(v
->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
);
993 v
->vehstatus
|= VS_STOPPED
;
997 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
998 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
999 * so each day, all vehicles are processes in DAY_TICKS steps.
1001 static void RunVehicleDayProc()
1003 if (_game_mode
!= GM_NORMAL
) return;
1005 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
1006 for (size_t i
= _date_fract
; i
< Vehicle::GetPoolSize(); i
+= DAY_TICKS
) {
1007 Vehicle
*v
= Vehicle::Get(i
);
1008 if (v
== NULL
) continue;
1010 /* Call the 32-day callback if needed */
1011 if ((v
->day_counter
& 0x1F) == 0 && v
->HasEngineType()) {
1012 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK
, 0, 0, v
->engine_type
, v
);
1013 if (callback
!= CALLBACK_FAILED
) {
1014 if (HasBit(callback
, 0)) {
1015 TriggerVehicle(v
, VEHICLE_TRIGGER_CALLBACK_32
); // Trigger vehicle trigger 10
1018 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
1019 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
1020 if (callback
!= 0) v
->First()->MarkDirty();
1022 if (callback
& ~3) ErrorUnknownCallbackResult(v
->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK
, callback
);
1026 /* This is called once per day for each vehicle, but not in the first tick of the day */
1031 static void ShowAutoReplaceAdviceMessage(const CommandCost
&res
, const Vehicle
*v
)
1033 StringID error_message
= res
.GetErrorMessage();
1034 if (error_message
== STR_ERROR_AUTOREPLACE_NOTHING_TO_DO
|| error_message
== INVALID_STRING_ID
) return;
1036 if (error_message
== STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY
) error_message
= STR_ERROR_AUTOREPLACE_MONEY_LIMIT
;
1039 if (error_message
== STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT
) {
1040 message
= error_message
;
1042 message
= STR_NEWS_VEHICLE_AUTORENEW_FAILED
;
1045 SetDParam(0, v
->index
);
1046 SetDParam(1, error_message
);
1047 AddVehicleAdviceNewsItem(message
, v
->index
);
1050 void CallVehicleTicks()
1052 _vehicles_to_autoreplace
.Clear();
1053 _vehicles_to_templatereplace
.Clear();
1055 RunVehicleDayProc();
1058 FOR_ALL_STATIONS(st
) LoadUnloadStation(st
);
1061 FOR_ALL_VEHICLES(v
) {
1062 /* Vehicle could be deleted in this tick */
1064 assert(Vehicle::Get(vehicle_index
) == NULL
);
1068 assert(Vehicle::Get(vehicle_index
) == v
);
1077 Vehicle
*front
= v
->First();
1079 if (v
->vcache
.cached_cargo_age_period
!= 0) {
1080 v
->cargo_age_counter
= min(v
->cargo_age_counter
, v
->vcache
.cached_cargo_age_period
);
1081 if (--v
->cargo_age_counter
== 0) {
1082 v
->cargo
.AgeCargo();
1083 v
->cargo_age_counter
= v
->vcache
.cached_cargo_age_period
;
1087 /* Do not play any sound when crashed */
1088 if (front
->vehstatus
& VS_CRASHED
) continue;
1090 /* Do not play any sound when in depot or tunnel */
1091 if (v
->vehstatus
& VS_HIDDEN
) continue;
1093 /* Do not play any sound when stopped */
1094 if ((front
->vehstatus
& VS_STOPPED
) && (front
->type
!= VEH_TRAIN
|| front
->cur_speed
== 0)) continue;
1096 /* Check vehicle type specifics */
1099 if (Train::From(v
)->IsWagon()) continue;
1103 if (!RoadVehicle::From(v
)->IsFrontEngine()) continue;
1107 if (!Aircraft::From(v
)->IsNormalAircraft()) continue;
1114 v
->motion_counter
+= front
->cur_speed
;
1115 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1116 if (GB(v
->motion_counter
, 0, 8) < front
->cur_speed
) PlayVehicleSound(v
, VSE_RUNNING
);
1118 /* Play an alternating running sound every 16 ticks */
1119 if (GB(v
->tick_counter
, 0, 4) == 0) {
1120 /* Play running sound when speed > 0 and not braking */
1121 bool running
= (front
->cur_speed
> 0) && !(front
->vehstatus
& (VS_STOPPED
| VS_TRAIN_SLOWING
));
1122 PlayVehicleSound(v
, running
? VSE_RUNNING_16
: VSE_STOPPED_16
);
1130 /* do Auto Replacement */
1131 Backup
<CompanyByte
> cur_company(_current_company
, FILE_LINE
);
1132 for (AutoreplaceMap::iterator it
= _vehicles_to_autoreplace
.Begin(); it
!= _vehicles_to_autoreplace
.End(); it
++) {
1134 /* Autoreplace needs the current company set as the vehicle owner */
1135 cur_company
.Change(v
->owner
);
1137 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1138 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1139 * they are already leaving the depot again before being replaced. */
1140 if (it
->second
) v
->vehstatus
&= ~VS_STOPPED
;
1142 /* Store the position of the effect as the vehicle pointer will become invalid later */
1147 const Company
*c
= Company::Get(_current_company
);
1148 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, (Money
)c
->settings
.engine_renew_money
));
1149 CommandCost res
= DoCommand(0, v
->index
, 0, DC_EXEC
, CMD_AUTOREPLACE_VEHICLE
);
1150 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES
, -(Money
)c
->settings
.engine_renew_money
));
1152 if (!IsLocalCompany()) continue;
1154 if (res
.Succeeded()) {
1155 ShowCostOrIncomeAnimation(x
, y
, z
, res
.GetCost());
1159 ShowAutoReplaceAdviceMessage(res
, v
);
1161 cur_company
.Restore();
1163 /* do Template Replacement */
1164 Backup
<CompanyByte
> tmpl_cur_company(_current_company
, FILE_LINE
);
1165 for (TemplateReplacementMap::iterator it
= _vehicles_to_templatereplace
.Begin(); it
!= _vehicles_to_templatereplace
.End(); it
++) {
1167 Train
*t
= it
->first
;
1169 /* Store the position of the effect as the vehicle pointer will become invalid later */
1174 tmpl_cur_company
.Change(t
->owner
);
1176 bool stayInDepot
= it
->second
;
1178 it
->first
->vehstatus
|= VS_STOPPED
;
1179 CommandCost res
= DoCommand(t
->tile
, t
->index
, stayInDepot
? 1 : 0, DC_EXEC
, CMD_TEMPLATE_REPLACE_VEHICLE
);
1181 if (!IsLocalCompany()) continue;
1183 if (res
.Succeeded()) {
1184 if (res
.GetCost() != 0) {
1185 ShowCostOrIncomeAnimation(x
, y
, z
, res
.GetCost());
1190 ShowAutoReplaceAdviceMessage(res
, t
);
1192 tmpl_cur_company
.Restore();
1196 * Add vehicle sprite for drawing to the screen.
1197 * @param v Vehicle to draw.
1199 static void DoDrawVehicle(const Vehicle
*v
)
1201 PaletteID pal
= PAL_NONE
;
1203 if (v
->vehstatus
& VS_DEFPAL
) pal
= (v
->vehstatus
& VS_CRASHED
) ? PALETTE_CRASH
: GetVehiclePalette(v
);
1205 /* Check whether the vehicle shall be transparent due to the game state */
1206 bool shadowed
= (v
->vehstatus
& (VS_SHADOW
| VS_HIDDEN
)) != 0;
1208 if (v
->type
== VEH_EFFECT
) {
1209 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1210 * However, transparent smoke and bubbles look weird, so always hide them. */
1211 TransparencyOption to
= EffectVehicle::From(v
)->GetTransparencyOption();
1212 if (to
!= TO_INVALID
&& (IsTransparencySet(to
) || IsInvisibilitySet(to
))) return;
1215 StartSpriteCombine();
1216 for (uint i
= 0; i
< v
->sprite_seq
.count
; ++i
) {
1217 PaletteID pal2
= v
->sprite_seq
.seq
[i
].pal
;
1218 if (!pal2
|| (v
->vehstatus
& VS_CRASHED
)) pal2
= pal
;
1219 AddSortableSpriteToDraw(v
->sprite_seq
.seq
[i
].sprite
, pal2
, v
->x_pos
+ v
->x_offs
, v
->y_pos
+ v
->y_offs
,
1220 v
->x_extent
, v
->y_extent
, v
->z_extent
, v
->z_pos
, shadowed
, v
->x_bb_offs
, v
->y_bb_offs
);
1226 * Add the vehicle sprites that should be drawn at a part of the screen.
1227 * @param dpi Rectangle being drawn.
1229 void ViewportAddVehicles(DrawPixelInfo
*dpi
)
1231 /* The bounding rectangle */
1232 const int l
= dpi
->left
;
1233 const int r
= dpi
->left
+ dpi
->width
;
1234 const int t
= dpi
->top
;
1235 const int b
= dpi
->top
+ dpi
->height
;
1237 /* The hash area to scan */
1240 if (dpi
->width
+ (MAX_VEHICLE_PIXEL_X
* ZOOM_LVL_BASE
) < GEN_HASHX_SIZE
) {
1241 xl
= GEN_HASHX(l
- MAX_VEHICLE_PIXEL_X
* ZOOM_LVL_BASE
);
1244 /* scan whole hash row */
1246 xu
= GEN_HASHX_MASK
;
1249 if (dpi
->height
+ (MAX_VEHICLE_PIXEL_Y
* ZOOM_LVL_BASE
) < GEN_HASHY_SIZE
) {
1250 yl
= GEN_HASHY(t
- MAX_VEHICLE_PIXEL_Y
* ZOOM_LVL_BASE
);
1253 /* scan whole column */
1255 yu
= GEN_HASHY_MASK
;
1258 for (int y
= yl
;; y
= (y
+ GEN_HASHY_INC
) & GEN_HASHY_MASK
) {
1259 for (int x
= xl
;; x
= (x
+ GEN_HASHX_INC
) & GEN_HASHX_MASK
) {
1260 const Vehicle
*v
= _vehicle_viewport_hash
[x
+ y
]; // already masked & 0xFFF
1264 l
<= v
->coord
.right
&&
1265 t
<= v
->coord
.bottom
&&
1266 r
>= v
->coord
.left
&&
1267 b
>= v
->coord
.top
) {
1270 v
= v
->hash_viewport_next
;
1280 void ViewportMapDrawVehicles(DrawPixelInfo
*dpi
)
1282 /* The bounding rectangle */
1283 const int l
= dpi
->left
;
1284 const int r
= dpi
->left
+ dpi
->width
;
1285 const int t
= dpi
->top
;
1286 const int b
= dpi
->top
+ dpi
->height
;
1288 /* The hash area to scan */
1291 if (dpi
->width
+ (70 * ZOOM_LVL_BASE
) < (1 << (7 + 6 + ZOOM_LVL_SHIFT
))) {
1292 xl
= GB(l
- (70 * ZOOM_LVL_BASE
), 7 + ZOOM_LVL_SHIFT
, 6);
1293 xu
= GB(r
, 7 + ZOOM_LVL_SHIFT
, 6);
1296 /* scan whole hash row */
1301 if (dpi
->height
+ (70 * ZOOM_LVL_BASE
) < (1 << (6 + 6 + ZOOM_LVL_SHIFT
))) {
1302 yl
= GB(t
- (70 * ZOOM_LVL_BASE
), 6 + ZOOM_LVL_SHIFT
, 6) << 6;
1303 yu
= GB(b
, 6 + ZOOM_LVL_SHIFT
, 6) << 6;
1306 /* scan whole column */
1311 const int w
= UnScaleByZoom(dpi
->width
, dpi
->zoom
);
1312 const int h
= UnScaleByZoom(dpi
->height
, dpi
->zoom
);
1313 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
1314 for (int y
= yl
;; y
= (y
+ (1 << 6)) & (0x3F << 6)) {
1315 for (int x
= xl
;; x
= (x
+ 1) & 0x3F) {
1316 const Vehicle
*v
= _vehicle_viewport_hash
[x
+ y
]; // already masked & 0xFFF
1319 if (!(v
->vehstatus
& (VS_HIDDEN
| VS_UNCLICKABLE
)) && (v
->type
!= VEH_EFFECT
)) {
1320 Point pt
= RemapCoords(v
->x_pos
, v
->y_pos
, v
->z_pos
);
1321 const int pixel_x
= UnScaleByZoomLower(pt
.x
- dpi
->left
, dpi
->zoom
);
1322 if (IsInsideMM(pixel_x
, 0, w
)) {
1323 const int pixel_y
= UnScaleByZoomLower(pt
.y
- dpi
->top
, dpi
->zoom
);
1324 if (IsInsideMM(pixel_y
, 0, h
))
1325 blitter
->SetPixel(dpi
->dst_ptr
, pixel_x
, pixel_y
, PC_WHITE
);
1328 v
= v
->hash_viewport_next
;
1339 * Find the vehicle close to the clicked coordinates.
1340 * @param vp Viewport clicked in.
1341 * @param x X coordinate in the viewport.
1342 * @param y Y coordinate in the viewport.
1343 * @return Closest vehicle, or \c NULL if none found.
1345 Vehicle
*CheckClickOnVehicle(const ViewPort
*vp
, int x
, int y
)
1347 Vehicle
*found
= NULL
, *v
;
1348 uint dist
, best_dist
= UINT_MAX
;
1350 if ((uint
)(x
-= vp
->left
) >= (uint
)vp
->width
|| (uint
)(y
-= vp
->top
) >= (uint
)vp
->height
) return NULL
;
1352 x
= ScaleByZoom(x
, vp
->zoom
) + vp
->virtual_left
;
1353 y
= ScaleByZoom(y
, vp
->zoom
) + vp
->virtual_top
;
1355 FOR_ALL_VEHICLES(v
) {
1356 if (((v
->vehstatus
& VS_UNCLICKABLE
) == 0) && v
->IsDrawn() &&
1357 x
>= v
->coord
.left
&& x
<= v
->coord
.right
&&
1358 y
>= v
->coord
.top
&& y
<= v
->coord
.bottom
) {
1361 abs(((v
->coord
.left
+ v
->coord
.right
) >> 1) - x
),
1362 abs(((v
->coord
.top
+ v
->coord
.bottom
) >> 1) - y
)
1365 if (dist
< best_dist
) {
1376 * Decrease the value of a vehicle.
1377 * @param v %Vehicle to devaluate.
1379 void DecreaseVehicleValue(Vehicle
*v
)
1381 v
->value
-= v
->value
>> 8;
1382 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1385 static const byte _breakdown_chance
[64] = {
1386 3, 3, 3, 3, 3, 3, 3, 3,
1387 4, 4, 5, 5, 6, 6, 7, 7,
1388 8, 8, 9, 9, 10, 10, 11, 11,
1389 12, 13, 13, 13, 13, 14, 15, 16,
1390 17, 19, 21, 25, 28, 31, 34, 37,
1391 40, 44, 48, 52, 56, 60, 64, 68,
1392 72, 80, 90, 100, 110, 120, 130, 140,
1393 150, 170, 190, 210, 230, 250, 250, 250,
1396 void CheckVehicleBreakdown(Vehicle
*v
)
1400 /* decrease reliability */
1401 v
->reliability
= rel
= max((rel_old
= v
->reliability
) - v
->reliability_spd_dec
, 0);
1402 if ((rel_old
>> 8) != (rel
>> 8)) SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1404 if (v
->breakdown_ctr
!= 0 || (v
->vehstatus
& VS_STOPPED
) ||
1405 _settings_game
.difficulty
.vehicle_breakdowns
< 1 ||
1406 v
->cur_speed
< 5 || _game_mode
== GM_MENU
) {
1410 uint32 r
= Random();
1412 /* increase chance of failure */
1413 int chance
= v
->breakdown_chance
+ 1;
1414 if (Chance16I(1, 25, r
)) chance
+= 25;
1415 v
->breakdown_chance
= min(255, chance
);
1417 /* calculate reliability value to use in comparison */
1418 rel
= v
->reliability
;
1419 if (v
->type
== VEH_SHIP
) rel
+= 0x6666;
1421 /* reduced breakdowns? */
1422 if (_settings_game
.difficulty
.vehicle_breakdowns
== 1) rel
+= 0x6666;
1424 /* check if to break down */
1425 if (_breakdown_chance
[(uint
)min(rel
, 0xffff) >> 10] <= v
->breakdown_chance
) {
1426 v
->breakdown_ctr
= GB(r
, 16, 6) + 0x3F;
1427 v
->breakdown_delay
= GB(r
, 24, 7) + 0x80;
1428 v
->breakdown_chance
= 0;
1433 * Handle all of the aspects of a vehicle breakdown
1434 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1435 * @return true iff the vehicle is stopped because of a breakdown
1436 * @note This function always returns false for aircraft, since these never stop for breakdowns
1438 bool Vehicle::HandleBreakdown()
1440 /* Possible states for Vehicle::breakdown_ctr
1441 * 0 - vehicle is running normally
1442 * 1 - vehicle is currently broken down
1443 * 2 - vehicle is going to break down now
1444 * >2 - vehicle is counting down to the actual breakdown event */
1445 switch (this->breakdown_ctr
) {
1450 this->breakdown_ctr
= 1;
1452 if (this->breakdowns_since_last_service
!= 255) {
1453 this->breakdowns_since_last_service
++;
1456 if (this->type
== VEH_AIRCRAFT
) {
1457 /* Aircraft just need this flag, the rest is handled elsewhere */
1458 this->vehstatus
|= VS_AIRCRAFT_BROKEN
;
1460 this->cur_speed
= 0;
1462 if (!PlayVehicleSound(this, VSE_BREAKDOWN
)) {
1463 bool train_or_ship
= this->type
== VEH_TRAIN
|| this->type
== VEH_SHIP
;
1464 SndPlayVehicleFx((_settings_game
.game_creation
.landscape
!= LT_TOYLAND
) ?
1465 (train_or_ship
? SND_10_TRAIN_BREAKDOWN
: SND_0F_VEHICLE_BREAKDOWN
) :
1466 (train_or_ship
? SND_3A_COMEDY_BREAKDOWN_2
: SND_35_COMEDY_BREAKDOWN
), this);
1469 if (!(this->vehstatus
& VS_HIDDEN
) && !HasBit(EngInfo(this->engine_type
)->misc_flags
, EF_NO_BREAKDOWN_SMOKE
)) {
1470 EffectVehicle
*u
= CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE
);
1471 if (u
!= NULL
) u
->animation_state
= this->breakdown_delay
* 2;
1475 this->MarkDirty(); // Update graphics after speed is zeroed
1476 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1477 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
1481 /* Aircraft breakdowns end only when arriving at the airport */
1482 if (this->type
== VEH_AIRCRAFT
) return false;
1484 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1485 if ((this->tick_counter
& (this->type
== VEH_TRAIN
? 3 : 1)) == 0) {
1486 if (--this->breakdown_delay
== 0) {
1487 this->breakdown_ctr
= 0;
1489 SetWindowDirty(WC_VEHICLE_VIEW
, this->index
);
1495 if (!this->current_order
.IsType(OT_LOADING
)) this->breakdown_ctr
--;
1501 * Update age of a vehicle.
1502 * @param v Vehicle to update.
1504 void AgeVehicle(Vehicle
*v
)
1506 /* Stop if a virtual vehicle */
1507 if (HasBit(v
->subtype
, GVSF_VIRTUAL
)) return;
1509 if (v
->age
< MAX_DAY
) {
1511 if (v
->IsPrimaryVehicle() && v
->age
== VEHICLE_PROFIT_MIN_AGE
+ 1) GroupStatistics::VehicleReachedProfitAge(v
);
1514 if (!v
->IsPrimaryVehicle() && (v
->type
!= VEH_TRAIN
|| !Train::From(v
)->IsEngine())) return;
1516 int age
= v
->age
- v
->max_age
;
1517 if (age
== DAYS_IN_LEAP_YEAR
* 0 || age
== DAYS_IN_LEAP_YEAR
* 1 ||
1518 age
== DAYS_IN_LEAP_YEAR
* 2 || age
== DAYS_IN_LEAP_YEAR
* 3 || age
== DAYS_IN_LEAP_YEAR
* 4) {
1519 v
->reliability_spd_dec
<<= 1;
1522 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
1524 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1525 if (v
->Previous() != NULL
|| v
->owner
!= _local_company
|| (v
->vehstatus
& VS_CRASHED
) != 0) return;
1527 /* Don't warn if a renew is active */
1528 if (Company::Get(v
->owner
)->settings
.engine_renew
&& v
->GetEngine()->company_avail
!= 0) return;
1531 if (age
== -DAYS_IN_LEAP_YEAR
) {
1532 str
= STR_NEWS_VEHICLE_IS_GETTING_OLD
;
1533 } else if (age
== 0) {
1534 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD
;
1535 } else if (age
> 0 && (age
% DAYS_IN_LEAP_YEAR
) == 0) {
1536 str
= STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND
;
1541 SetDParam(0, v
->index
);
1542 AddVehicleAdviceNewsItem(str
, v
->index
);
1546 * Calculates how full a vehicle is.
1547 * @param front The front vehicle of the consist to check.
1548 * @param colour The string to show depending on if we are unloading or loading
1549 * @return A percentage of how full the Vehicle is.
1550 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1551 * if the vehicle is completely empty or full.
1552 * This is useful for both display and conditional orders.
1554 uint8
CalcPercentVehicleFilled(const Vehicle
*front
, StringID
*colour
)
1560 bool loading
= false;
1562 bool is_loading
= front
->current_order
.IsType(OT_LOADING
);
1564 /* The station may be NULL when the (colour) string does not need to be set. */
1565 const Station
*st
= Station::GetIfValid(front
->last_station_visited
);
1566 assert(colour
== NULL
|| (st
!= NULL
&& is_loading
));
1568 bool order_no_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_NO_LOAD
);
1569 bool order_full_load
= is_loading
&& (front
->current_order
.GetLoadType() & OLFB_FULL_LOAD
);
1571 /* Count up max and used */
1572 for (const Vehicle
*v
= front
; v
!= NULL
; v
= v
->Next()) {
1573 count
+= v
->cargo
.StoredCount();
1574 max
+= v
->cargo_cap
;
1575 if (v
->cargo_cap
!= 0 && colour
!= NULL
) {
1576 unloading
+= HasBit(v
->vehicle_flags
, VF_CARGO_UNLOADING
) ? 1 : 0;
1577 loading
|= !order_no_load
&&
1578 (order_full_load
|| st
->goods
[v
->cargo_type
].HasRating()) &&
1579 !HasBit(v
->vehicle_flags
, VF_LOADING_FINISHED
) && !HasBit(v
->vehicle_flags
, VF_STOP_LOADING
);
1584 if (colour
!= NULL
) {
1585 if (unloading
== 0 && loading
) {
1586 *colour
= STR_PERCENT_UP
;
1587 } else if (unloading
== 0 && !loading
) {
1588 *colour
= STR_PERCENT_NONE
;
1589 } else if (cars
== unloading
|| !loading
) {
1590 *colour
= STR_PERCENT_DOWN
;
1592 *colour
= STR_PERCENT_UP_DOWN
;
1596 /* Train without capacity */
1597 if (max
== 0) return 100;
1599 /* Return the percentage */
1600 if (count
* 2 < max
) {
1601 /* Less than 50%; round up, so that 0% means really empty. */
1602 return CeilDiv(count
* 100, max
);
1604 /* More than 50%; round down, so that 100% means really full. */
1605 return (count
* 100) / max
;
1610 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1611 * @param v Vehicle that entered a depot.
1613 void VehicleEnterDepot(Vehicle
*v
)
1615 /* Always work with the front of the vehicle */
1616 assert(v
== v
->First());
1620 Train
*t
= Train::From(v
);
1621 SetWindowClassesDirty(WC_TRAINS_LIST
);
1622 /* Clear path reservation */
1623 SetDepotReservation(t
->tile
, false);
1624 if (_settings_client
.gui
.show_track_reservation
) MarkTileDirtyByTile(t
->tile
, ZOOM_LVL_DRAW_MAP
);
1626 UpdateSignalsOnSegment(t
->tile
, INVALID_DIAGDIR
, t
->owner
);
1627 t
->wait_counter
= 0;
1628 t
->force_proceed
= TFP_NONE
;
1629 ClrBit(t
->flags
, VRF_TOGGLE_REVERSE
);
1630 t
->ConsistChanged(CCF_ARRANGE
);
1631 t
->reverse_distance
= 0;
1636 SetWindowClassesDirty(WC_ROADVEH_LIST
);
1640 SetWindowClassesDirty(WC_SHIPS_LIST
);
1641 Ship
*ship
= Ship::From(v
);
1642 ship
->state
= TRACK_BIT_DEPOT
;
1643 ship
->UpdateCache();
1644 ship
->UpdateViewport(true, true);
1645 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1650 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
1651 HandleAircraftEnterHangar(Aircraft::From(v
));
1653 default: NOT_REACHED();
1655 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1657 if (v
->type
!= VEH_TRAIN
) {
1658 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1659 * 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 */
1660 InvalidateWindowData(WC_VEHICLE_DEPOT
, v
->tile
);
1662 SetWindowDirty(WC_VEHICLE_DEPOT
, v
->tile
);
1664 v
->vehstatus
|= VS_HIDDEN
;
1667 VehicleServiceInDepot(v
);
1669 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1670 TriggerVehicle(v
, VEHICLE_TRIGGER_DEPOT
);
1673 if (v
->current_order
.IsType(OT_GOTO_DEPOT
)) {
1674 SetWindowDirty(WC_VEHICLE_VIEW
, v
->index
);
1676 if (v
->type
!= VEH_TRAIN
&& v
->current_order
.GetDepotOrderType() == ODTF_MANUAL
) {
1677 // Check first if the vehicle has any depot in its order list. If yes then we're heading for a specific depot.
1678 // Don't stop if this one isn't it.
1679 bool has_depot_in_orders
= false;
1681 for (int i
= 0; i
< v
->GetNumOrders(); ++i
) {
1682 Order
* order
= v
->orders
.list
->GetOrderAt(i
);
1684 bool isRegularOrder
= (order
->GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) != 0;
1685 bool isDepotOrder
= order
->GetType() == OT_GOTO_DEPOT
;
1687 if (isRegularOrder
&& isDepotOrder
) {
1688 has_depot_in_orders
= true;
1693 if (has_depot_in_orders
)
1695 if ((v
->type
== VEH_AIRCRAFT
&& Station::GetByTile(v
->dest_tile
)->index
!= Station::GetByTile(v
->tile
)->index
) ||
1696 (v
->type
!= VEH_AIRCRAFT
&& v
->dest_tile
!= v
->tile
)) {
1697 /* We are heading for another depot, keep driving. */
1703 const Order
*real_order
= v
->GetOrder(v
->cur_real_order_index
);
1705 /* Test whether we are heading for this depot. If not, do nothing.
1706 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1707 if ((v
->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) &&
1708 real_order
!= NULL
&& !(real_order
->GetDepotActionType() & ODATFB_NEAREST_DEPOT
) &&
1709 (v
->type
== VEH_AIRCRAFT
? v
->current_order
.GetDestination() != GetStationIndex(v
->tile
) : v
->dest_tile
!= v
->tile
)) {
1710 /* We are heading for another depot, keep driving. */
1714 if (v
->current_order
.IsRefit()) {
1715 Backup
<CompanyByte
> cur_company(_current_company
, v
->owner
, FILE_LINE
);
1716 CommandCost cost
= DoCommand(v
->tile
, v
->index
, v
->current_order
.GetRefitCargo() | 0xFF << 8, DC_EXEC
, GetCmdRefitVeh(v
));
1717 cur_company
.Restore();
1719 if (cost
.Failed()) {
1720 _vehicles_to_autoreplace
[v
] = false;
1721 if (v
->owner
== _local_company
) {
1722 /* Notify the user that we stopped the vehicle */
1723 SetDParam(0, v
->index
);
1724 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED
, v
->index
);
1726 } else if (cost
.GetCost() != 0) {
1727 v
->profit_this_year
-= cost
.GetCost() << 8;
1728 if (v
->owner
== _local_company
) {
1729 ShowCostOrIncomeAnimation(v
->x_pos
, v
->y_pos
, v
->z_pos
, cost
.GetCost());
1734 /* Handle the ODTFB_PART_OF_ORDERS case. If there is a timetabled wait time, hold the vehicle, otherwise skip to the next order.
1735 Note that if there is a only a travel_time, but no wait_time defined for the order, and the vehicle arrives to the depot sooner as scheduled,
1736 he doesn't wait in it, as it would in stations. Thus, the original behavior is maintained if there's no defined wait_time.*/
1737 if (v
->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) {
1738 v
->DeleteUnreachedImplicitOrders();
1739 UpdateVehicleTimetable(v
, true);
1740 if (v
->current_order
.IsWaitTimetabled() && !(v
->current_order
.GetDepotActionType() & ODATFB_HALT
)) {
1741 v
->current_order
.MakeWaiting();
1742 v
->HandleAutomaticTimetableSeparation();
1743 v
->current_order
.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION
);
1747 UpdateVehicleTimetable(v
, false);
1748 v
->IncrementImplicitOrderIndex();
1751 if (v
->current_order
.GetDepotActionType() & ODATFB_HALT
) {
1752 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1753 _vehicles_to_autoreplace
[v
] = false;
1754 /* Invalidate last_loading_station. As the link from the station
1755 * before the stop to the station after the stop can't be predicted
1756 * we shouldn't construct it when the vehicle visits the next stop. */
1757 v
->last_loading_station
= INVALID_STATION
;
1758 ClrBit(v
->vehicle_flags
, VF_LAST_LOAD_ST_SEP
);
1759 if (v
->owner
== _local_company
) {
1760 SetDParam(0, v
->index
);
1761 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING
+ v
->type
, v
->index
);
1763 AI::NewEvent(v
->owner
, new ScriptEventVehicleWaitingInDepot(v
->index
));
1764 v
->MarkSeparationInvalid();
1766 v
->current_order
.MakeDummy();
1772 * Update the position of the vehicle. This will update the hash that tells
1773 * which vehicles are on a tile.
1775 void Vehicle::UpdatePosition()
1777 UpdateVehicleTileHash(this, false);
1781 * Update the vehicle on the viewport, updating the right hash and setting the
1783 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1785 void Vehicle::UpdateViewport(bool dirty
)
1788 this->sprite_seq
.GetBounds(&new_coord
);
1790 Point pt
= RemapCoords(this->x_pos
+ this->x_offs
, this->y_pos
+ this->y_offs
, this->z_pos
);
1791 new_coord
.left
+= pt
.x
;
1792 new_coord
.top
+= pt
.y
;
1793 new_coord
.right
+= pt
.x
+ 2 * ZOOM_LVL_BASE
;
1794 new_coord
.bottom
+= pt
.y
+ 2 * ZOOM_LVL_BASE
;
1796 UpdateVehicleViewportHash(this, new_coord
.left
, new_coord
.top
);
1798 Rect old_coord
= this->coord
;
1799 this->coord
= new_coord
;
1802 if (old_coord
.left
== INVALID_COORD
) {
1803 this->MarkAllViewportsDirty();
1805 ::MarkAllViewportsDirty(
1806 min(old_coord
.left
, this->coord
.left
),
1807 min(old_coord
.top
, this->coord
.top
),
1808 max(old_coord
.right
, this->coord
.right
),
1809 max(old_coord
.bottom
, this->coord
.bottom
),
1810 this->type
!= VEH_EFFECT
? ZOOM_LVL_END
: ZOOM_LVL_DRAW_MAP
);
1816 * Update the position of the vehicle, and update the viewport.
1818 void Vehicle::UpdatePositionAndViewport()
1820 this->UpdatePosition();
1821 this->UpdateViewport(true);
1825 * Marks viewports dirty where the vehicle's image is.
1827 void Vehicle::MarkAllViewportsDirty() const
1829 ::MarkAllViewportsDirty(this->coord
.left
, this->coord
.top
, this->coord
.right
, this->coord
.bottom
, this->type
!= VEH_EFFECT
? ZOOM_LVL_END
: ZOOM_LVL_DRAW_MAP
);
1833 * Get position information of a vehicle when moving one pixel in the direction it is facing
1834 * @param v Vehicle to move
1835 * @return Position information after the move
1837 GetNewVehiclePosResult
GetNewVehiclePos(const Vehicle
*v
)
1839 static const int8 _delta_coord
[16] = {
1840 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1841 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1844 int x
= v
->x_pos
+ _delta_coord
[v
->direction
];
1845 int y
= v
->y_pos
+ _delta_coord
[v
->direction
+ 8];
1847 GetNewVehiclePosResult gp
;
1850 gp
.old_tile
= v
->tile
;
1851 gp
.new_tile
= TileVirtXY(x
, y
);
1855 static const Direction _new_direction_table
[] = {
1856 DIR_N
, DIR_NW
, DIR_W
,
1857 DIR_NE
, DIR_SE
, DIR_SW
,
1858 DIR_E
, DIR_SE
, DIR_S
1861 Direction
GetDirectionTowards(const Vehicle
*v
, int x
, int y
)
1865 if (y
>= v
->y_pos
) {
1866 if (y
!= v
->y_pos
) i
+= 3;
1870 if (x
>= v
->x_pos
) {
1871 if (x
!= v
->x_pos
) i
++;
1875 Direction dir
= v
->direction
;
1877 DirDiff dirdiff
= DirDifference(_new_direction_table
[i
], dir
);
1878 if (dirdiff
== DIRDIFF_SAME
) return dir
;
1879 return ChangeDir(dir
, dirdiff
> DIRDIFF_REVERSE
? DIRDIFF_45LEFT
: DIRDIFF_45RIGHT
);
1883 * Call the tile callback function for a vehicle entering a tile
1884 * @param v Vehicle entering the tile
1885 * @param tile Tile entered
1886 * @param x X position
1887 * @param y Y position
1888 * @return Some meta-data over the to be entered tile.
1889 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1891 VehicleEnterTileStatus
VehicleEnterTile(Vehicle
*v
, TileIndex tile
, int x
, int y
)
1893 return _tile_type_procs
[GetTileType(tile
)]->vehicle_enter_tile_proc(v
, tile
, x
, y
);
1897 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1898 * struct initialization, except after each call to this->NextID() the returned value
1899 * is assigned to a vehicle.
1900 * @param type type of vehicle
1901 * @param owner owner of vehicles
1903 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type
, CompanyID owner
) : cache(NULL
), maxid(0), curid(0)
1907 FOR_ALL_VEHICLES(v
) {
1908 if (v
->type
== type
&& v
->owner
== owner
) {
1909 this->maxid
= max
<UnitID
>(this->maxid
, v
->unitnumber
);
1913 if (this->maxid
== 0) return;
1915 /* Reserving 'maxid + 2' because we need:
1916 * - space for the last item (with v->unitnumber == maxid)
1917 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1918 this->cache
= CallocT
<bool>(this->maxid
+ 2);
1920 /* Fill the cache */
1921 FOR_ALL_VEHICLES(v
) {
1922 if (v
->type
== type
&& v
->owner
== owner
) {
1923 this->cache
[v
->unitnumber
] = true;
1928 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
1929 UnitID
FreeUnitIDGenerator::NextID()
1931 if (this->maxid
<= this->curid
) return ++this->curid
;
1933 while (this->cache
[++this->curid
]) { } // it will stop, we reserved more space than needed
1939 * Get an unused unit number for a vehicle (if allowed).
1940 * @param type Type of vehicle
1941 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1943 UnitID
GetFreeUnitNumber(VehicleType type
)
1945 /* Check whether it is allowed to build another vehicle. */
1948 case VEH_TRAIN
: max_veh
= _settings_game
.vehicle
.max_trains
; break;
1949 case VEH_ROAD
: max_veh
= _settings_game
.vehicle
.max_roadveh
; break;
1950 case VEH_SHIP
: max_veh
= _settings_game
.vehicle
.max_ships
; break;
1951 case VEH_AIRCRAFT
: max_veh
= _settings_game
.vehicle
.max_aircraft
; break;
1952 default: NOT_REACHED();
1955 const Company
*c
= Company::Get(_current_company
);
1956 if (c
->group_all
[type
].num_vehicle
>= max_veh
) return UINT16_MAX
; // Currently already at the limit, no room to make a new one.
1958 FreeUnitIDGenerator
gen(type
, _current_company
);
1960 return gen
.NextID();
1965 * Check whether we can build infrastructure for the given
1966 * vehicle type. This to disable building stations etc. when
1967 * you are not allowed/able to have the vehicle type yet.
1968 * @param type the vehicle type to check this for
1969 * @return true if there is any reason why you may build
1970 * the infrastructure for the given vehicle type
1972 bool CanBuildVehicleInfrastructure(VehicleType type
)
1974 assert(IsCompanyBuildableVehicleType(type
));
1976 if (!Company::IsValidID(_local_company
)) return false;
1977 if (!_settings_client
.gui
.disable_unsuitable_building
) return true;
1981 case VEH_TRAIN
: max
= _settings_game
.vehicle
.max_trains
; break;
1982 case VEH_ROAD
: max
= _settings_game
.vehicle
.max_roadveh
; break;
1983 case VEH_SHIP
: max
= _settings_game
.vehicle
.max_ships
; break;
1984 case VEH_AIRCRAFT
: max
= _settings_game
.vehicle
.max_aircraft
; break;
1985 default: NOT_REACHED();
1988 /* We can build vehicle infrastructure when we may build the vehicle type */
1990 /* Can we actually build the vehicle type? */
1992 FOR_ALL_ENGINES_OF_TYPE(e
, type
) {
1993 if (HasBit(e
->company_avail
, _local_company
)) return true;
1998 /* We should be able to build infrastructure when we have the actual vehicle type */
2000 FOR_ALL_VEHICLES(v
) {
2001 if (v
->owner
== _local_company
&& v
->type
== type
) return true;
2009 * Determines the #LiveryScheme for a vehicle.
2010 * @param engine_type Engine of the vehicle.
2011 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
2012 * @param v the vehicle, \c NULL if in purchase list etc.
2013 * @return livery scheme to use.
2015 LiveryScheme
GetEngineLiveryScheme(EngineID engine_type
, EngineID parent_engine_type
, const Vehicle
*v
)
2017 CargoID cargo_type
= v
== NULL
? (CargoID
)CT_INVALID
: v
->cargo_type
;
2018 const Engine
*e
= Engine::Get(engine_type
);
2020 default: NOT_REACHED();
2022 if (v
!= NULL
&& parent_engine_type
!= INVALID_ENGINE
&& (UsesWagonOverride(v
) || (v
->IsArticulatedPart() && e
->u
.rail
.railveh_type
!= RAILVEH_WAGON
))) {
2023 /* Wagonoverrides use the colour scheme of the front engine.
2024 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
2025 engine_type
= parent_engine_type
;
2026 e
= Engine::Get(engine_type
);
2027 /* Note: Luckily cargo_type is not needed for engines */
2030 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
2031 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
2032 if (e
->u
.rail
.railveh_type
== RAILVEH_WAGON
) {
2033 if (!CargoSpec::Get(cargo_type
)->is_freight
) {
2034 if (parent_engine_type
== INVALID_ENGINE
) {
2035 return LS_PASSENGER_WAGON_STEAM
;
2037 switch (RailVehInfo(parent_engine_type
)->engclass
) {
2038 default: NOT_REACHED();
2039 case EC_STEAM
: return LS_PASSENGER_WAGON_STEAM
;
2040 case EC_DIESEL
: return LS_PASSENGER_WAGON_DIESEL
;
2041 case EC_ELECTRIC
: return LS_PASSENGER_WAGON_ELECTRIC
;
2042 case EC_MONORAIL
: return LS_PASSENGER_WAGON_MONORAIL
;
2043 case EC_MAGLEV
: return LS_PASSENGER_WAGON_MAGLEV
;
2047 return LS_FREIGHT_WAGON
;
2050 bool is_mu
= HasBit(e
->info
.misc_flags
, EF_RAIL_IS_MU
);
2052 switch (e
->u
.rail
.engclass
) {
2053 default: NOT_REACHED();
2054 case EC_STEAM
: return LS_STEAM
;
2055 case EC_DIESEL
: return is_mu
? LS_DMU
: LS_DIESEL
;
2056 case EC_ELECTRIC
: return is_mu
? LS_EMU
: LS_ELECTRIC
;
2057 case EC_MONORAIL
: return LS_MONORAIL
;
2058 case EC_MAGLEV
: return LS_MAGLEV
;
2063 /* Always use the livery of the front */
2064 if (v
!= NULL
&& parent_engine_type
!= INVALID_ENGINE
) {
2065 engine_type
= parent_engine_type
;
2066 e
= Engine::Get(engine_type
);
2067 cargo_type
= v
->First()->cargo_type
;
2069 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
2070 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
2072 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
2073 if (HasBit(e
->info
.misc_flags
, EF_ROAD_TRAM
)) {
2075 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_TRAM
: LS_FREIGHT_TRAM
;
2078 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_BUS
: LS_TRUCK
;
2082 if (cargo_type
== CT_INVALID
) cargo_type
= e
->GetDefaultCargoType();
2083 if (cargo_type
== CT_INVALID
) cargo_type
= CT_GOODS
; // The vehicle does not carry anything, let's pick some freight cargo
2084 return IsCargoInClass(cargo_type
, CC_PASSENGERS
) ? LS_PASSENGER_SHIP
: LS_FREIGHT_SHIP
;
2087 switch (e
->u
.air
.subtype
) {
2088 case AIR_HELI
: return LS_HELICOPTER
;
2089 case AIR_CTOL
: return LS_SMALL_PLANE
;
2090 case AIR_CTOL
| AIR_FAST
: return LS_LARGE_PLANE
;
2091 default: NOT_REACHED();
2097 * Determines the livery for a vehicle.
2098 * @param engine_type EngineID of the vehicle
2099 * @param company Owner of the vehicle
2100 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
2101 * @param v the vehicle. NULL if in purchase list etc.
2102 * @param livery_setting The livery settings to use for acquiring the livery information.
2103 * @return livery to use
2105 const Livery
*GetEngineLivery(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
, byte livery_setting
)
2107 const Company
*c
= Company::Get(company
);
2108 LiveryScheme scheme
= LS_DEFAULT
;
2110 /* The default livery is always available for use, but its in_use flag determines
2111 * whether any _other_ liveries are in use. */
2112 if (c
->livery
[LS_DEFAULT
].in_use
&& (livery_setting
== LIT_ALL
|| (livery_setting
== LIT_COMPANY
&& company
== _local_company
))) {
2113 /* Determine the livery scheme to use */
2114 scheme
= GetEngineLiveryScheme(engine_type
, parent_engine_type
, v
);
2116 /* Switch back to the default scheme if the resolved scheme is not in use */
2117 if (!c
->livery
[scheme
].in_use
) scheme
= LS_DEFAULT
;
2120 return &c
->livery
[scheme
];
2124 static PaletteID
GetEngineColourMap(EngineID engine_type
, CompanyID company
, EngineID parent_engine_type
, const Vehicle
*v
)
2126 PaletteID map
= (v
!= NULL
) ? v
->colourmap
: PAL_NONE
;
2128 /* Return cached value if any */
2129 if (map
!= PAL_NONE
) return map
;
2131 const Engine
*e
= Engine::Get(engine_type
);
2133 /* Check if we should use the colour map callback */
2134 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_COLOUR_REMAP
)) {
2135 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING
, 0, 0, engine_type
, v
);
2136 /* Failure means "use the default two-colour" */
2137 if (callback
!= CALLBACK_FAILED
) {
2138 assert_compile(PAL_NONE
== 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2139 map
= GB(callback
, 0, 14);
2140 /* If bit 14 is set, then the company colours are applied to the
2141 * map else it's returned as-is. */
2142 if (!HasBit(callback
, 14)) {
2144 if (v
!= NULL
) const_cast<Vehicle
*>(v
)->colourmap
= map
;
2150 bool twocc
= HasBit(e
->info
.misc_flags
, EF_USES_2CC
);
2152 if (map
== PAL_NONE
) map
= twocc
? (PaletteID
)SPR_2CCMAP_BASE
: (PaletteID
)PALETTE_RECOLOUR_START
;
2154 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2155 if (!Company::IsValidID(company
)) return map
;
2157 const Livery
*livery
= GetEngineLivery(engine_type
, company
, parent_engine_type
, v
, _settings_client
.gui
.liveries
);
2159 map
+= livery
->colour1
;
2160 if (twocc
) map
+= livery
->colour2
* 16;
2163 if (v
!= NULL
) const_cast<Vehicle
*>(v
)->colourmap
= map
;
2168 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2169 * @param engine_type ID of engine
2170 * @param company ID of company
2171 * @return A ready-to-use palette modifier
2173 PaletteID
GetEnginePalette(EngineID engine_type
, CompanyID company
)
2175 return GetEngineColourMap(engine_type
, company
, INVALID_ENGINE
, NULL
);
2179 * Get the colour map for a vehicle.
2180 * @param v Vehicle to get colour map for
2181 * @return A ready-to-use palette modifier
2183 PaletteID
GetVehiclePalette(const Vehicle
*v
)
2185 if (v
->IsGroundVehicle()) {
2186 return GetEngineColourMap(v
->engine_type
, v
->owner
, v
->GetGroundVehicleCache()->first_engine
, v
);
2189 return GetEngineColourMap(v
->engine_type
, v
->owner
, INVALID_ENGINE
, v
);
2193 * Delete all implicit orders which were not reached.
2195 void Vehicle::DeleteUnreachedImplicitOrders()
2197 if (this->IsGroundVehicle()) {
2198 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2199 if (HasBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
)) {
2200 /* Do not delete orders, only skip them */
2201 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2202 this->cur_implicit_order_index
= this->cur_real_order_index
;
2203 InvalidateVehicleOrder(this, 0);
2208 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2209 while (order
!= NULL
) {
2210 if (this->cur_implicit_order_index
== this->cur_real_order_index
) break;
2212 if (order
->IsType(OT_IMPLICIT
)) {
2213 DeleteOrder(this, this->cur_implicit_order_index
);
2214 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2215 order
= this->GetOrder(this->cur_implicit_order_index
);
2217 /* Skip non-implicit orders, e.g. service-orders */
2218 order
= order
->next
;
2219 this->cur_implicit_order_index
++;
2223 if (order
== NULL
) {
2224 order
= this->GetOrder(0);
2225 this->cur_implicit_order_index
= 0;
2231 * Increase capacity for all link stats associated with vehicles in the given consist.
2232 * @param st Station to get the link stats from.
2233 * @param front First vehicle in the consist.
2234 * @param next_station_id Station the consist will be travelling to next.
2236 static void VehicleIncreaseStats(const Vehicle
*front
)
2238 for (const Vehicle
*v
= front
; v
!= NULL
; v
= v
->Next()) {
2239 StationID last_loading_station
= HasBit(front
->vehicle_flags
, VF_LAST_LOAD_ST_SEP
) ? v
->last_loading_station
: front
->last_loading_station
;
2240 if (v
->refit_cap
> 0 &&
2241 last_loading_station
!= INVALID_STATION
&&
2242 last_loading_station
!= front
->last_station_visited
&&
2243 ((front
->current_order
.GetCargoLoadType(v
->cargo_type
) & OLFB_NO_LOAD
) == 0 ||
2244 (front
->current_order
.GetCargoUnloadType(v
->cargo_type
) & OUFB_NO_UNLOAD
) == 0)) {
2245 /* The cargo count can indeed be higher than the refit_cap if
2246 * wagons have been auto-replaced and subsequently auto-
2247 * refitted to a higher capacity. The cargo gets redistributed
2248 * among the wagons in that case.
2249 * As usage is not such an important figure anyway we just
2250 * ignore the additional cargo then.*/
2251 IncreaseStats(Station::Get(last_loading_station
), v
->cargo_type
, front
->last_station_visited
, v
->refit_cap
,
2252 min(v
->refit_cap
, v
->cargo
.StoredCount()), EUM_INCREASE
);
2258 * Prepare everything to begin the loading when arriving at a station.
2259 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2261 void Vehicle::BeginLoading()
2263 assert(IsTileType(this->tile
, MP_STATION
) || this->type
== VEH_SHIP
);
2265 if (this->current_order
.IsType(OT_GOTO_STATION
) &&
2266 this->current_order
.GetDestination() == this->last_station_visited
) {
2267 this->DeleteUnreachedImplicitOrders();
2269 /* Now both order indices point to the destination station, and we can start loading */
2270 this->current_order
.MakeLoading(true);
2271 UpdateVehicleTimetable(this, true);
2273 /* Furthermore add the Non Stop flag to mark that this station
2274 * is the actual destination of the vehicle, which is (for example)
2275 * necessary to be known for HandleTrainLoading to determine
2276 * whether the train is lost or not; not marking a train lost
2277 * that arrives at random stations is bad. */
2278 this->current_order
.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION
);
2281 /* We weren't scheduled to stop here. Insert an implicit order
2282 * to show that we are stopping here.
2283 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2284 * the 'wrong' terminal when skipping orders etc. */
2285 Order
*in_list
= this->GetOrder(this->cur_implicit_order_index
);
2286 if (this->IsGroundVehicle() &&
2287 (in_list
== NULL
|| !in_list
->IsType(OT_IMPLICIT
) ||
2288 in_list
->GetDestination() != this->last_station_visited
)) {
2289 bool suppress_implicit_orders
= HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS
);
2290 /* Do not create consecutive duplicates of implicit orders */
2291 Order
*prev_order
= this->cur_implicit_order_index
> 0 ? this->GetOrder(this->cur_implicit_order_index
- 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL
);
2292 if (prev_order
== NULL
||
2293 (!prev_order
->IsType(OT_IMPLICIT
) && !prev_order
->IsType(OT_GOTO_STATION
)) ||
2294 prev_order
->GetDestination() != this->last_station_visited
) {
2296 /* Prefer deleting implicit orders instead of inserting new ones,
2297 * so test whether the right order follows later. In case of only
2298 * implicit orders treat the last order in the list like an
2299 * explicit one, except if the overall number of orders surpasses
2300 * IMPLICIT_ORDER_ONLY_CAP. */
2301 int target_index
= this->cur_implicit_order_index
;
2303 while (target_index
!= this->cur_real_order_index
|| this->GetNumManualOrders() == 0) {
2304 const Order
*order
= this->GetOrder(target_index
);
2305 if (order
== NULL
) break; // No orders.
2306 if (order
->IsType(OT_IMPLICIT
) && order
->GetDestination() == this->last_station_visited
) {
2311 if (target_index
>= this->orders
.list
->GetNumOrders()) {
2312 if (this->GetNumManualOrders() == 0 &&
2313 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP
) {
2318 if (target_index
== this->cur_implicit_order_index
) break; // Avoid infinite loop.
2322 if (suppress_implicit_orders
) {
2323 /* Skip to the found order */
2324 this->cur_implicit_order_index
= target_index
;
2325 InvalidateVehicleOrder(this, 0);
2327 /* Delete all implicit orders up to the station we just reached */
2328 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2329 while (!order
->IsType(OT_IMPLICIT
) || order
->GetDestination() != this->last_station_visited
) {
2330 if (order
->IsType(OT_IMPLICIT
)) {
2331 DeleteOrder(this, this->cur_implicit_order_index
);
2332 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2333 order
= this->GetOrder(this->cur_implicit_order_index
);
2335 /* Skip non-implicit orders, e.g. service-orders */
2336 order
= order
->next
;
2337 this->cur_implicit_order_index
++;
2341 if (order
== NULL
) {
2342 order
= this->GetOrder(0);
2343 this->cur_implicit_order_index
= 0;
2345 assert(order
!= NULL
);
2348 } else if (!suppress_implicit_orders
&&
2349 ((this->orders
.list
== NULL
? OrderList::CanAllocateItem() : this->orders
.list
->GetNumOrders() < MAX_VEH_ORDER_ID
)) &&
2350 Order::CanAllocateItem()) {
2351 /* Insert new implicit order */
2352 Order
*implicit_order
= new Order();
2353 implicit_order
->MakeImplicit(this->last_station_visited
);
2354 InsertOrder(this, implicit_order
, this->cur_implicit_order_index
);
2355 if (this->cur_implicit_order_index
> 0) --this->cur_implicit_order_index
;
2357 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2358 * Reenable it for this vehicle */
2359 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2360 ClrBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2364 this->current_order
.MakeLoading(false);
2367 this->HandleAutomaticTimetableSeparation();
2369 VehicleIncreaseStats(this);
2371 PrepareUnload(this);
2373 SetWindowDirty(GetWindowClassForVehicleType(this->type
), this->owner
);
2374 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2375 SetWindowDirty(WC_VEHICLE_DETAILS
, this->index
);
2376 SetWindowDirty(WC_STATION_VIEW
, this->last_station_visited
);
2378 Station::Get(this->last_station_visited
)->MarkTilesDirty(true);
2379 this->cur_speed
= 0;
2384 * Handles the automatic timetable separation from initialization to setting of the lateness counter at the correct first order.
2386 void Vehicle::HandleAutomaticTimetableSeparation()
2388 /* If all requirements for separation are met, we can initialize it. */
2389 if (!_settings_game
.order
.automatic_timetable_separation
) return;
2391 if (!this->IsOrderListShared() || !this->orders
.list
->IsCompleteTimetable()) return;
2393 int first_wait_index
= -1;
2395 for (int i
= 0; i
< this->orders
.list
->GetNumOrders(); ++i
) {
2396 Order
* order
= this->orders
.list
->GetOrderAt(i
);
2398 if (order
->IsWaitTimetabled() && !order
->IsType(OT_IMPLICIT
)) {
2399 first_wait_index
= i
;
2404 if (this->cur_implicit_order_index
!= first_wait_index
) return;
2406 if (!this->orders
.list
->IsSeparationValid()) {
2407 this->orders
.list
->InitializeSeparation();
2408 this->lateness_counter
= this->orders
.list
->SeparateVehicle();
2409 SetWindowDirty(WC_VEHICLE_TIMETABLE
, this->index
);
2413 this->lateness_counter
= this->orders
.list
->SeparateVehicle();
2415 if (this->lateness_counter
> 0)
2417 this->orders
.list
->InitializeSeparation();
2418 this->lateness_counter
= this->orders
.list
->SeparateVehicle();
2419 SetWindowDirty(WC_VEHICLE_TIMETABLE
, this->index
);
2425 * Return all reserved cargo packets to the station and reset all packets
2426 * staged for transfer.
2427 * @param st the station where the reserved packets should go.
2429 void Vehicle::CancelReservation(StationID next
, Station
*st
)
2431 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->next
) {
2432 VehicleCargoList
&cargo
= v
->cargo
;
2433 if (cargo
.ActionCount(VehicleCargoList::MTA_LOAD
) > 0) {
2434 DEBUG(misc
, 1, "cancelling cargo reservation");
2435 cargo
.Return(UINT_MAX
, &st
->goods
[v
->cargo_type
].cargo
, next
);
2436 cargo
.SetTransferLoadPlace(st
->xy
);
2442 uint32
Vehicle::GetLastLoadingStationValidCargoMask() const
2444 if (!HasBit(this->vehicle_flags
, VF_LAST_LOAD_ST_SEP
)) {
2445 return (this->last_loading_station
!= INVALID_STATION
) ? ~0 : 0;
2447 uint32 cargo_mask
= 0;
2448 for (const Vehicle
*u
= this; u
!= NULL
; u
= u
->Next()) {
2449 if (u
->cargo_type
< NUM_CARGO
&& u
->last_loading_station
!= INVALID_STATION
) {
2450 SetBit(cargo_mask
, u
->cargo_type
);
2458 * Perform all actions when leaving a station.
2459 * @pre this->current_order.IsType(OT_LOADING)
2461 void Vehicle::LeaveStation()
2463 assert(this->current_order
.IsType(OT_LOADING
));
2465 delete this->cargo_payment
;
2466 assert(this->cargo_payment
== NULL
); // cleared by ~CargoPayment
2468 /* Only update the timetable if the vehicle was supposed to stop here. */
2469 if (this->current_order
.GetNonStopType() != ONSF_STOP_EVERYWHERE
) UpdateVehicleTimetable(this, false);
2471 uint32 cargoes_can_load_unload
= this->current_order
.FilterLoadUnloadTypeCargoMask([&](const Order
*o
, CargoID cargo
) {
2472 return ((o
->GetCargoLoadType(cargo
) & OLFB_NO_LOAD
) == 0) || ((o
->GetCargoUnloadType(cargo
) & OUFB_NO_UNLOAD
) == 0);
2474 uint32 has_cargo_mask
= this->GetLastLoadingStationValidCargoMask();
2475 uint32 cargoes_can_leave_with_cargo
= FilterCargoMask([&](CargoID cargo
) {
2476 return this->current_order
.CanLeaveWithCargo(HasBit(has_cargo_mask
, cargo
), cargo
);
2477 }, cargoes_can_load_unload
);
2479 if (cargoes_can_load_unload
!= 0) {
2480 if (cargoes_can_leave_with_cargo
!= 0) {
2481 /* Refresh next hop stats to make sure we've done that at least once
2482 * during the stop and that refit_cap == cargo_cap for each vehicle in
2484 this->ResetRefitCaps();
2485 LinkRefresher::Run(this, true, false, cargoes_can_leave_with_cargo
);
2488 if (cargoes_can_leave_with_cargo
== (uint32
) ~0) {
2489 /* can leave with all cargoes */
2491 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2492 this->last_loading_station
= this->last_station_visited
;
2493 ClrBit(this->vehicle_flags
, VF_LAST_LOAD_ST_SEP
);
2494 } else if (cargoes_can_leave_with_cargo
== 0) {
2495 /* can leave with no cargoes */
2497 /* if the vehicle couldn't load and had to unload or transfer everything
2498 * set the last loading station to invalid as it will leave empty. */
2499 this->last_loading_station
= INVALID_STATION
;
2500 ClrBit(this->vehicle_flags
, VF_LAST_LOAD_ST_SEP
);
2502 /* mix of cargoes loadable or could not leave with all cargoes */
2504 /* NB: this is saved here as we overwrite it on the first iteration of the loop below */
2505 StationID head_last_loading_station
= this->last_loading_station
;
2506 for (Vehicle
*u
= this; u
!= NULL
; u
= u
->Next()) {
2507 StationID last_loading_station
= HasBit(this->vehicle_flags
, VF_LAST_LOAD_ST_SEP
) ? u
->last_loading_station
: head_last_loading_station
;
2508 if (u
->cargo_type
< NUM_CARGO
&& HasBit(cargoes_can_load_unload
, u
->cargo_type
)) {
2509 if (HasBit(cargoes_can_leave_with_cargo
, u
->cargo_type
)) {
2510 u
->last_loading_station
= this->last_station_visited
;
2512 u
->last_loading_station
= INVALID_STATION
;
2515 u
->last_loading_station
= last_loading_station
;
2518 SetBit(this->vehicle_flags
, VF_LAST_LOAD_ST_SEP
);
2523 int last_loading_order_index
= -1;
2525 // Reverse iterate through the orders list and find the first (i.e. last) order that is of loading type.
2526 for (int i
= this->orders
.list
->GetNumOrders() - 1; i
>= 0; --i
) {
2527 Order
* order
= this->orders
.list
->GetOrderAt(i
);
2529 bool can_load_or_unload
= false;
2531 if ((order
->IsType(OT_GOTO_STATION
) || order
->IsType(OT_IMPLICIT
)) &&
2532 (order
->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION
) == 0) {
2533 if (order
->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD
|| order
->GetLoadType() == OLFB_CARGO_TYPE_LOAD
) {
2534 can_load_or_unload
= true;
2535 } else if ((order
->GetLoadType() & OLFB_NO_LOAD
) == 0 || (order
->GetUnloadType() & OUFB_NO_UNLOAD
) == 0) {
2536 can_load_or_unload
= true;
2540 if (can_load_or_unload
&& !(order
->GetLoadType() & OLFB_NO_LOAD
)) {
2541 last_loading_order_index
= i
;
2546 if (last_loading_order_index
>= 0 && last_loading_order_index
< this->orders
.list
->GetNumOrders()) {
2548 Order
* current_real_order
= this->orders
.list
->GetOrderAt(this->cur_real_order_index
);
2550 bool can_load_or_unload
= false;
2552 if ((current_real_order
->IsType(OT_GOTO_STATION
) || current_real_order
->IsType(OT_IMPLICIT
)) &&
2553 (current_real_order
->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION
) == 0) {
2554 if (current_real_order
->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD
|| current_real_order
->GetLoadType() == OLFB_CARGO_TYPE_LOAD
) {
2555 can_load_or_unload
= true;
2557 else if ((current_real_order
->GetLoadType() & OLFB_NO_LOAD
) == 0 || (current_real_order
->GetUnloadType() & OUFB_NO_UNLOAD
) == 0) {
2558 can_load_or_unload
= true;
2562 bool current_order_was_load_order
= (can_load_or_unload
&& !(current_real_order
->GetLoadType() & OLFB_NO_LOAD
));
2564 if (current_order_was_load_order
)
2566 int8 occupancy
= (int8
)CalcPercentVehicleFilled(this, NULL
);
2567 station_occupancies
.push_back(occupancy
);
2570 bool wasLastLoadingOrder
= this->cur_real_order_index
== last_loading_order_index
;
2572 if (wasLastLoadingOrder
)
2574 if (station_occupancies
.size() != 0)
2578 std::vector
<int8
>::const_iterator it
;
2579 for (it
= station_occupancies
.begin(); it
!= station_occupancies
.end(); ++it
)
2582 station_occupancies
.clear();
2587 station_occupancies
.clear();
2590 this->current_order
.MakeLeaveStation();
2591 Station
*st
= Station::Get(this->last_station_visited
);
2592 this->CancelReservation(INVALID_STATION
, st
);
2593 st
->loading_vehicles
.erase(std::remove(st
->loading_vehicles
.begin(), st
->loading_vehicles
.end(), this), st
->loading_vehicles
.end());
2595 HideFillingPercent(&this->fill_percent_te_id
);
2597 if (this->type
== VEH_TRAIN
&& !(this->vehstatus
& VS_CRASHED
)) {
2598 /* Trigger station animation (trains only) */
2599 if (IsTileType(this->tile
, MP_STATION
)) {
2600 TriggerStationRandomisation(st
, this->tile
, SRT_TRAIN_DEPARTS
);
2601 TriggerStationAnimation(st
, this->tile
, SAT_TRAIN_DEPARTS
);
2604 SetBit(Train::From(this)->flags
, VRF_LEAVING_STATION
);
2611 * Reset all refit_cap in the consist to cargo_cap.
2613 void Vehicle::ResetRefitCaps()
2615 for (Vehicle
*v
= this; v
!= NULL
; v
= v
->Next()) v
->refit_cap
= v
->cargo_cap
;
2619 * Handle the loading of the vehicle; when not it skips through dummy
2620 * orders and does nothing in all other cases.
2621 * @param mode is the non-first call for this vehicle in this tick?
2623 void Vehicle::HandleLoading(bool mode
)
2625 OrderType current_order_type
= this->current_order
.GetType();
2627 switch (current_order_type
) {
2629 uint wait_time
= max(this->current_order
.GetTimetabledWait() - this->lateness_counter
, 0);
2631 bool has_manual_depot_order
= (HasBit(this->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
) || HasBit(this->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
));
2633 if (!has_manual_depot_order
) {
2634 /* Not the first call for this tick, or still loading */
2635 if (mode
|| !HasBit(this->vehicle_flags
, VF_LOADING_FINISHED
) || this->current_order_time
< wait_time
) return;
2638 this->PlayLeaveStationSound();
2640 this->LeaveStation();
2642 /* Only advance to next order if we just loaded at the current one */
2643 const Order
*order
= this->GetOrder(this->cur_implicit_order_index
);
2644 if (order
== NULL
||
2645 (!order
->IsType(OT_IMPLICIT
) && !order
->IsType(OT_GOTO_STATION
)) ||
2646 order
->GetDestination() != this->last_station_visited
) {
2652 case OT_DUMMY
: break;
2657 this->IncrementImplicitOrderIndex();
2661 * Handle the waiting time everywhere else as in stations (basically in depot but, eventually, also elsewhere ?)
2662 * Function is called when order's wait_time is defined.
2663 * @param stop_waiting should we stop waiting (or definitely avoid) even if there is still time left to wait ?
2665 void Vehicle::HandleWaiting(bool stop_waiting
)
2667 switch (this->current_order
.GetType()) {
2669 uint wait_time
= max(this->current_order
.GetTimetabledWait() - this->lateness_counter
, 0);
2670 /* Vehicles holds on until waiting Timetabled time expires. */
2671 if (!stop_waiting
&& this->current_order_time
< wait_time
) {
2675 /* When wait_time is expired, we move on. */
2676 UpdateVehicleTimetable(this, false);
2677 this->IncrementImplicitOrderIndex();
2678 this->current_order
.MakeDummy();
2688 * Send this vehicle to the depot using the given command(s).
2689 * @param flags the command flags (like execute and such).
2690 * @param command the command to execute.
2691 * @return the cost of the depot action.
2693 CommandCost
Vehicle::SendToDepot(DoCommandFlag flags
, DepotCommand command
)
2695 CommandCost ret
= CheckOwnership(this->owner
);
2696 if (ret
.Failed()) return ret
;
2698 if (this->vehstatus
& VS_CRASHED
) return CMD_ERROR
;
2699 if (this->IsStoppedInDepot()) return CMD_ERROR
;
2701 if (HasBit(this->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
) || HasBit(this->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
)) {
2702 bool service_only
= (command
& DEPOT_SERVICE
) != 0;
2703 if (service_only
!= HasBit(this->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
)) {
2704 /* We called with a different DEPOT_SERVICE setting.
2705 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2706 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2707 if (flags
& DC_EXEC
) {
2708 ClrBit(this->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
);
2709 ClrBit(this->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
);
2710 SetBit(this->vehicle_flags
, service_only
? VF_SHOULD_SERVICE_AT_DEPOT
: VF_SHOULD_GOTO_DEPOT
);
2711 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2713 return CommandCost();
2716 if (command
& DEPOT_DONT_CANCEL
) return CMD_ERROR
; // Requested no cancellation of depot orders
2718 if(flags
& DC_EXEC
) {
2719 // Cancel the forced depot order.
2720 ClrBit(this->vehicle_flags
, VF_SHOULD_GOTO_DEPOT
);
2721 ClrBit(this->vehicle_flags
, VF_SHOULD_SERVICE_AT_DEPOT
);
2722 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2724 return CommandCost();
2726 } else if (this->current_order
.IsType(OT_GOTO_DEPOT
) && this->current_order
.GetDepotOrderType() == ODTF_MANUAL
) {
2727 bool current_service_only
= !((this->current_order
.GetDepotActionType() & ODATFB_HALT
) != 0);
2728 bool service_only
= (command
& DEPOT_SERVICE
) != 0;
2729 if (current_service_only
!= service_only
) {
2730 /* We called with a different DEPOT_SERVICE setting.
2731 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2732 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2733 if (flags
& DC_EXEC
) {
2734 this->current_order
.SetDepotOrderType(ODTF_MANUAL
);
2735 this->current_order
.SetDepotActionType(service_only
? ODATF_SERVICE_ONLY
: ODATFB_HALT
);
2736 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2738 return CommandCost();
2741 if (command
& DEPOT_DONT_CANCEL
) return CMD_ERROR
; // Requested no cancellation of depot orders
2742 if (flags
& DC_EXEC
) {
2743 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2744 * then skip to the next order; effectively canceling this forced service */
2745 if (this->current_order
.GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) this->IncrementRealOrderIndex();
2747 if (this->IsGroundVehicle()) {
2748 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2749 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2752 this->current_order
.MakeDummy();
2753 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2755 return CommandCost();
2759 DestinationID destination
;
2761 bool foundDepotInOrders
= false;
2762 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
};
2764 // Check first if the vehicle has any depot in its order list. Prefer that over the closest one.
2766 if (this->orders
.list
!= NULL
) {
2767 for (int i
= 0; i
< this->orders
.list
->GetNumOrders(); ++i
) {
2768 Order
* order
= this->orders
.list
->GetOrderAt(i
);
2770 bool isRegularOrder
= (order
->GetDepotOrderType() & ODTFB_PART_OF_ORDERS
) != 0;
2771 bool isDepotOrder
= order
->GetType() == OT_GOTO_DEPOT
;
2773 if (isRegularOrder
&& isDepotOrder
) {
2774 destination
= order
->GetDestination();
2775 if (this->type
== VEH_AIRCRAFT
) {
2776 Station
* st
= Station::Get(destination
);
2777 if (st
!= NULL
&& st
->airport
.HasHangar() && CanVehicleUseStation(this, st
)) {
2779 foundDepotInOrders
= true;
2784 location
= Depot::Get(destination
)->xy
;
2786 foundDepotInOrders
= true;
2793 if (!foundDepotInOrders
&& !this->FindClosestDepot(&location
, &destination
, &reverse
)) return_cmd_error(no_depot
[this->type
]);
2795 if (flags
& DC_EXEC
) {
2796 if (!(foundDepotInOrders
&& this->type
== VEH_TRAIN
)) {
2797 // The OT_LOADING status of trains with depots in their order list will be handled separately in the HandleLoading() method.
2798 if (this->current_order
.IsType(OT_LOADING
)) this->LeaveStation();
2801 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2802 uint16
&gv_flags
= this->GetGroundVehicleFlags();
2803 SetBit(gv_flags
, GVF_SUPPRESS_IMPLICIT_ORDERS
);
2806 if (foundDepotInOrders
&& this->type
== VEH_TRAIN
) {
2807 bool service_only
= (command
& DEPOT_SERVICE
) != 0;
2808 SetBit(this->vehicle_flags
, service_only
? VF_SHOULD_SERVICE_AT_DEPOT
: VF_SHOULD_GOTO_DEPOT
);
2809 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2811 this->dest_tile
= location
;
2812 this->current_order
.MakeGoToDepot(destination
, ODTF_MANUAL
);
2814 if (!(command
& DEPOT_SERVICE
)) this->current_order
.SetDepotActionType(ODATFB_HALT
);
2815 SetWindowWidgetDirty(WC_VEHICLE_VIEW
, this->index
, WID_VV_START_STOP
);
2817 /* If there is no depot in front, reverse automatically (trains only) */
2818 if (this->type
== VEH_TRAIN
&& reverse
) DoCommand(this->tile
, this->index
, 0, DC_EXEC
, CMD_REVERSE_TRAIN_DIRECTION
);
2820 if (this->type
== VEH_AIRCRAFT
) {
2821 Aircraft
*a
= Aircraft::From(this);
2822 if (a
->state
== FLYING
&& a
->targetairport
!= destination
) {
2823 /* The aircraft is now heading for a different hangar than the next in the orders */
2824 extern void AircraftNextAirportPos_and_Order(Aircraft
*a
);
2825 AircraftNextAirportPos_and_Order(a
);
2831 return CommandCost();
2836 * Update the cached visual effect.
2837 * @param allow_power_change true if the wagon-is-powered-state may change.
2839 void Vehicle::UpdateVisualEffect(bool allow_power_change
)
2841 bool powered_before
= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2842 const Engine
*e
= this->GetEngine();
2844 /* Evaluate properties */
2847 case VEH_TRAIN
: visual_effect
= e
->u
.rail
.visual_effect
; break;
2848 case VEH_ROAD
: visual_effect
= e
->u
.road
.visual_effect
; break;
2849 case VEH_SHIP
: visual_effect
= e
->u
.ship
.visual_effect
; break;
2850 default: visual_effect
= 1 << VE_DISABLE_EFFECT
; break;
2853 /* Check powered wagon / visual effect callback */
2854 if (HasBit(e
->info
.callback_mask
, CBM_VEHICLE_VISUAL_EFFECT
) && !HasBit(this->subtype
, GVSF_VIRTUAL
)) {
2855 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT
, 0, 0, this->engine_type
, this);
2857 if (callback
!= CALLBACK_FAILED
) {
2858 if (callback
>= 0x100 && e
->GetGRF()->grf_version
>= 8) ErrorUnknownCallbackResult(e
->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT
, callback
);
2860 callback
= GB(callback
, 0, 8);
2861 /* Avoid accidentally setting 'visual_effect' to the default value
2862 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2863 if (callback
== VE_DEFAULT
) {
2864 assert(HasBit(callback
, VE_DISABLE_EFFECT
));
2865 SB(callback
, VE_TYPE_START
, VE_TYPE_COUNT
, 0);
2867 visual_effect
= callback
;
2871 /* Apply default values */
2872 if (visual_effect
== VE_DEFAULT
||
2873 (!HasBit(visual_effect
, VE_DISABLE_EFFECT
) && GB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
) == VE_TYPE_DEFAULT
)) {
2874 /* Only train engines have default effects.
2875 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2876 if (e
->type
!= VEH_TRAIN
|| e
->u
.rail
.railveh_type
== RAILVEH_WAGON
|| !IsInsideMM(e
->u
.rail
.engclass
, EC_STEAM
, EC_MONORAIL
)) {
2877 if (visual_effect
== VE_DEFAULT
) {
2878 visual_effect
= 1 << VE_DISABLE_EFFECT
;
2880 SetBit(visual_effect
, VE_DISABLE_EFFECT
);
2883 if (visual_effect
== VE_DEFAULT
) {
2884 /* Also set the offset */
2885 visual_effect
= (VE_OFFSET_CENTRE
- (e
->u
.rail
.engclass
== EC_STEAM
? 4 : 0)) << VE_OFFSET_START
;
2887 SB(visual_effect
, VE_TYPE_START
, VE_TYPE_COUNT
, e
->u
.rail
.engclass
- EC_STEAM
+ VE_TYPE_STEAM
);
2891 this->vcache
.cached_vis_effect
= visual_effect
;
2893 if (!allow_power_change
&& powered_before
!= HasBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
)) {
2894 ToggleBit(this->vcache
.cached_vis_effect
, VE_DISABLE_WAGON_POWER
);
2895 ShowNewGrfVehicleError(this->engine_type
, STR_NEWGRF_BROKEN
, STR_NEWGRF_BROKEN_POWERED_WAGON
, GBUG_VEH_POWERED_WAGON
, false);
2899 static const int8 _vehicle_smoke_pos
[8] = {
2900 1, 1, 1, 0, -1, -1, -1, 0
2904 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2905 * @param v Vehicle to create effects for.
2907 static void SpawnAdvancedVisualEffect(const Vehicle
*v
)
2909 uint16 callback
= GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT
, 0, Random(), v
->engine_type
, v
);
2910 if (callback
== CALLBACK_FAILED
) return;
2912 uint count
= GB(callback
, 0, 2);
2913 bool auto_center
= HasBit(callback
, 13);
2914 bool auto_rotate
= !HasBit(callback
, 14);
2918 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2919 if (v
->type
== VEH_ROAD
) l_center
= -(int)(VEHICLE_LENGTH
- RoadVehicle::From(v
)->gcache
.cached_veh_length
) / 2;
2921 /* For trains: Compute offset from vehicle position to sprite position */
2922 if (v
->type
== VEH_TRAIN
) l_center
= (VEHICLE_LENGTH
- Train::From(v
)->gcache
.cached_veh_length
) / 2;
2925 Direction l_dir
= v
->direction
;
2926 if (v
->type
== VEH_TRAIN
&& HasBit(Train::From(v
)->flags
, VRF_REVERSE_DIRECTION
)) l_dir
= ReverseDir(l_dir
);
2927 Direction t_dir
= ChangeDir(l_dir
, DIRDIFF_90RIGHT
);
2929 int8 x_center
= _vehicle_smoke_pos
[l_dir
] * l_center
;
2930 int8 y_center
= _vehicle_smoke_pos
[t_dir
] * l_center
;
2932 for (uint i
= 0; i
< count
; i
++) {
2933 uint32 reg
= GetRegister(0x100 + i
);
2934 uint type
= GB(reg
, 0, 8);
2935 int8 x
= GB(reg
, 8, 8);
2936 int8 y
= GB(reg
, 16, 8);
2937 int8 z
= GB(reg
, 24, 8);
2942 x
= _vehicle_smoke_pos
[l_dir
] * l
+ _vehicle_smoke_pos
[t_dir
] * t
;
2943 y
= _vehicle_smoke_pos
[t_dir
] * l
- _vehicle_smoke_pos
[l_dir
] * t
;
2948 case 0xF1: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_STEAM_SMOKE
); break;
2949 case 0xF2: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_DIESEL_SMOKE
); break;
2950 case 0xF3: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_ELECTRIC_SPARK
); break;
2951 case 0xFA: CreateEffectVehicleRel(v
, x_center
+ x
, y_center
+ y
, z
, EV_BREAKDOWN_SMOKE_AIRCRAFT
); break;
2958 uint16
ReversingDistanceTargetSpeed(const Train
*v
);
2961 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2962 * @pre this->IsPrimaryVehicle()
2964 void Vehicle::ShowVisualEffect() const
2966 assert(this->IsPrimaryVehicle());
2969 /* Do not show any smoke when:
2970 * - vehicle smoke is disabled by the player
2971 * - the vehicle is slowing down or stopped (by the player)
2972 * - the vehicle is moving very slowly
2974 if (_settings_game
.vehicle
.smoke_amount
== 0 ||
2975 this->vehstatus
& (VS_TRAIN_SLOWING
| VS_STOPPED
) ||
2976 this->cur_speed
< 2) {
2980 /* Use the speed as limited by underground and orders. */
2981 uint max_speed
= this->GetCurrentMaxSpeed();
2983 if (this->type
== VEH_TRAIN
) {
2984 const Train
*t
= Train::From(this);
2985 /* For trains, do not show any smoke when:
2986 * - the train is reversing
2987 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2988 * - is approaching a reversing point and its speed is equal to maximum approach speed
2990 if (HasBit(t
->flags
, VRF_REVERSING
) ||
2991 (IsRailStationTile(t
->tile
) && t
->IsFrontEngine() && t
->current_order
.ShouldStopAtStation(t
, GetStationIndex(t
->tile
)) &&
2992 t
->cur_speed
>= max_speed
) ||
2993 (t
->reverse_distance
>= 1 && t
->cur_speed
>= ReversingDistanceTargetSpeed(t
))) {
2998 const Vehicle
*v
= this;
3001 bool advanced
= HasBit(v
->vcache
.cached_vis_effect
, VE_ADVANCED_EFFECT
);
3002 int effect_offset
= GB(v
->vcache
.cached_vis_effect
, VE_OFFSET_START
, VE_OFFSET_COUNT
) - VE_OFFSET_CENTRE
;
3003 VisualEffectSpawnModel effect_model
= VESM_NONE
;
3005 effect_offset
= VE_OFFSET_CENTRE
;
3006 effect_model
= (VisualEffectSpawnModel
)GB(v
->vcache
.cached_vis_effect
, 0, VE_ADVANCED_EFFECT
);
3007 if (effect_model
>= VESM_END
) effect_model
= VESM_NONE
; // unknown spawning model
3009 effect_model
= (VisualEffectSpawnModel
)GB(v
->vcache
.cached_vis_effect
, VE_TYPE_START
, VE_TYPE_COUNT
);
3010 assert(effect_model
!= (VisualEffectSpawnModel
)VE_TYPE_DEFAULT
); // should have been resolved by UpdateVisualEffect
3011 assert_compile((uint
)VESM_STEAM
== (uint
)VE_TYPE_STEAM
);
3012 assert_compile((uint
)VESM_DIESEL
== (uint
)VE_TYPE_DIESEL
);
3013 assert_compile((uint
)VESM_ELECTRIC
== (uint
)VE_TYPE_ELECTRIC
);
3016 /* Show no smoke when:
3017 * - Smoke has been disabled for this vehicle
3018 * - The vehicle is not visible
3019 * - The vehicle is under a bridge
3020 * - The vehicle is on a depot tile
3021 * - The vehicle is on a tunnel tile
3022 * - The vehicle is a train engine that is currently unpowered */
3023 if (effect_model
== VESM_NONE
||
3024 v
->vehstatus
& VS_HIDDEN
||
3025 IsBridgeAbove(v
->tile
) ||
3026 IsDepotTile(v
->tile
) ||
3027 IsTunnelTile(v
->tile
) ||
3028 (v
->type
== VEH_TRAIN
&&
3029 !HasPowerOnRail(Train::From(v
)->railtype
, GetTileRailType(v
->tile
)))) {
3033 EffectVehicleType evt
= EV_END
;
3034 switch (effect_model
) {
3036 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
3037 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
3038 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
3040 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
3041 if (GB(v
->tick_counter
, 0, ((4 >> _settings_game
.vehicle
.smoke_amount
) + ((this->cur_speed
* 3) / max_speed
))) == 0) {
3042 evt
= EV_STEAM_SMOKE
;
3047 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
3048 * when smoke emission stops.
3049 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
3050 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
3051 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
3052 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
3053 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
3054 * maximum speed no diesel_smoke is emitted.
3056 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
3057 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
3058 int power_weight_effect
= 0;
3059 if (v
->type
== VEH_TRAIN
) {
3060 power_weight_effect
= (32 >> (Train::From(this)->gcache
.cached_power
>> 10)) - (32 >> (Train::From(this)->gcache
.cached_weight
>> 9));
3062 if (this->cur_speed
< (max_speed
>> (2 >> _settings_game
.vehicle
.smoke_amount
)) &&
3063 Chance16((64 - ((this->cur_speed
<< 5) / max_speed
) + power_weight_effect
), (512 >> _settings_game
.vehicle
.smoke_amount
))) {
3064 evt
= EV_DIESEL_SMOKE
;
3070 /* Electric train's spark - more often occurs when train is departing (more load)
3071 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
3072 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
3073 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
3075 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
3076 if (GB(v
->tick_counter
, 0, 2) == 0 &&
3077 Chance16((6 - ((this->cur_speed
<< 2) / max_speed
)), (360 >> _settings_game
.vehicle
.smoke_amount
))) {
3078 evt
= EV_ELECTRIC_SPARK
;
3086 if (evt
!= EV_END
&& advanced
) {
3088 SpawnAdvancedVisualEffect(v
);
3089 } else if (evt
!= EV_END
) {
3092 /* The effect offset is relative to a point 4 units behind the vehicle's
3093 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
3094 * correction factor. */
3095 if (v
->type
== VEH_TRAIN
) effect_offset
+= (VEHICLE_LENGTH
- Train::From(v
)->gcache
.cached_veh_length
) / 2;
3097 int x
= _vehicle_smoke_pos
[v
->direction
] * effect_offset
;
3098 int y
= _vehicle_smoke_pos
[(v
->direction
+ 2) % 8] * effect_offset
;
3100 if (v
->type
== VEH_TRAIN
&& HasBit(Train::From(v
)->flags
, VRF_REVERSE_DIRECTION
)) {
3105 CreateEffectVehicleRel(v
, x
, y
, 10, evt
);
3107 } while ((v
= v
->Next()) != NULL
);
3109 if (sound
) PlayVehicleSound(this, VSE_VISUAL_EFFECT
);
3113 * Set the next vehicle of this vehicle.
3114 * @param next the next vehicle. NULL removes the next vehicle.
3116 void Vehicle::SetNext(Vehicle
*next
)
3118 assert(this != next
);
3120 if (this->next
!= NULL
) {
3121 /* We had an old next vehicle. Update the first and previous pointers */
3122 for (Vehicle
*v
= this->next
; v
!= NULL
; v
= v
->Next()) {
3123 v
->first
= this->next
;
3125 this->next
->previous
= NULL
;
3130 if (this->next
!= NULL
) {
3131 /* A new next vehicle. Update the first and previous pointers */
3132 if (this->next
->previous
!= NULL
) this->next
->previous
->next
= NULL
;
3133 this->next
->previous
= this;
3134 for (Vehicle
*v
= this->next
; v
!= NULL
; v
= v
->Next()) {
3135 v
->first
= this->first
;
3141 * Adds this vehicle to a shared vehicle chain.
3142 * @param shared_chain a vehicle of the chain with shared vehicles.
3143 * @pre !this->IsOrderListShared()
3145 void Vehicle::AddToShared(Vehicle
*shared_chain
)
3147 assert(this->previous_shared
== NULL
&& this->next_shared
== NULL
);
3149 if (shared_chain
->orders
.list
== NULL
) {
3150 assert(shared_chain
->previous_shared
== NULL
);
3151 assert(shared_chain
->next_shared
== NULL
);
3152 this->orders
.list
= shared_chain
->orders
.list
= new OrderList(NULL
, shared_chain
);
3155 this->next_shared
= shared_chain
->next_shared
;
3156 this->previous_shared
= shared_chain
;
3158 shared_chain
->next_shared
= this;
3160 if (this->next_shared
!= NULL
) this->next_shared
->previous_shared
= this;
3162 shared_chain
->orders
.list
->AddVehicle(this);
3163 shared_chain
->orders
.list
->MarkSeparationInvalid();
3167 * Removes the vehicle from the shared order list.
3169 void Vehicle::RemoveFromShared()
3171 /* Remember if we were first and the old window number before RemoveVehicle()
3172 * as this changes first if needed. */
3173 bool were_first
= (this->FirstShared() == this);
3174 VehicleListIdentifier
vli(VL_SHARED_ORDERS
, this->type
, this->owner
, this->FirstShared()->index
);
3176 this->orders
.list
->MarkSeparationInvalid();
3177 this->orders
.list
->RemoveVehicle(this);
3180 /* We are not the first shared one, so only relink our previous one. */
3181 this->previous_shared
->next_shared
= this->NextShared();
3184 if (this->next_shared
!= NULL
) this->next_shared
->previous_shared
= this->previous_shared
;
3187 if (this->orders
.list
->GetNumVehicles() == 1) {
3188 /* When there is only one vehicle, remove the shared order list window. */
3189 DeleteWindowById(GetWindowClassForVehicleType(this->type
), vli
.Pack());
3190 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS
);
3191 } else if (were_first
) {
3192 /* If we were the first one, update to the new first one.
3193 * Note: FirstShared() is already the new first */
3194 InvalidateWindowData(GetWindowClassForVehicleType(this->type
), vli
.Pack(), this->FirstShared()->index
| (1U << 31));
3197 this->next_shared
= NULL
;
3198 this->previous_shared
= NULL
;
3201 void VehiclesYearlyLoop()
3204 FOR_ALL_VEHICLES(v
) {
3205 if (v
->IsPrimaryVehicle()) {
3206 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
3207 Money profit
= v
->GetDisplayProfitThisYear();
3208 if (v
->age
>= 730 && profit
< 0) {
3209 if (_settings_client
.gui
.vehicle_income_warn
&& v
->owner
== _local_company
) {
3210 SetDParam(0, v
->index
);
3211 SetDParam(1, profit
);
3212 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE
, v
->index
);
3214 AI::NewEvent(v
->owner
, new ScriptEventVehicleUnprofitable(v
->index
));
3217 v
->profit_last_year
= v
->profit_this_year
;
3218 v
->profit_lifetime
+= v
->profit_this_year
;
3219 v
->profit_this_year
= 0;
3220 SetWindowDirty(WC_VEHICLE_DETAILS
, v
->index
);
3223 GroupStatistics::UpdateProfits();
3224 SetWindowClassesDirty(WC_TRAINS_LIST
);
3225 SetWindowClassesDirty(WC_SHIPS_LIST
);
3226 SetWindowClassesDirty(WC_ROADVEH_LIST
);
3227 SetWindowClassesDirty(WC_AIRCRAFT_LIST
);
3232 * Can this station be used by the given engine type?
3233 * @param engine_type the type of vehicles to test
3234 * @param st the station to test for
3235 * @return true if and only if the vehicle of the type can use this station.
3236 * @note For road vehicles the Vehicle is needed to determine whether it can
3237 * use the station. This function will return true for road vehicles
3238 * when at least one of the facilities is available.
3240 bool CanVehicleUseStation(EngineID engine_type
, const Station
*st
)
3242 const Engine
*e
= Engine::GetIfValid(engine_type
);
3247 return (st
->facilities
& FACIL_TRAIN
) != 0;
3250 /* For road vehicles we need the vehicle to know whether it can actually
3251 * use the station, but if it doesn't have facilities for RVs it is
3252 * certainly not possible that the station can be used. */
3253 return (st
->facilities
& (FACIL_BUS_STOP
| FACIL_TRUCK_STOP
)) != 0;
3256 return (st
->facilities
& FACIL_DOCK
) != 0;
3259 return (st
->facilities
& FACIL_AIRPORT
) != 0 &&
3260 (st
->airport
.GetFTA()->flags
& (e
->u
.air
.subtype
& AIR_CTOL
? AirportFTAClass::AIRPLANES
: AirportFTAClass::HELICOPTERS
)) != 0;
3268 * Can this station be used by the given vehicle?
3269 * @param v the vehicle to test
3270 * @param st the station to test for
3271 * @return true if and only if the vehicle can use this station.
3273 bool CanVehicleUseStation(const Vehicle
*v
, const Station
*st
)
3275 if (v
->type
== VEH_ROAD
) return st
->GetPrimaryRoadStop(RoadVehicle::From(v
)) != NULL
;
3277 return CanVehicleUseStation(v
->engine_type
, st
);
3281 * Access the ground vehicle cache of the vehicle.
3282 * @pre The vehicle is a #GroundVehicle.
3283 * @return #GroundVehicleCache of the vehicle.
3285 GroundVehicleCache
*Vehicle::GetGroundVehicleCache()
3287 assert(this->IsGroundVehicle());
3288 if (this->type
== VEH_TRAIN
) {
3289 return &Train::From(this)->gcache
;
3291 return &RoadVehicle::From(this)->gcache
;
3296 * Access the ground vehicle cache of the vehicle.
3297 * @pre The vehicle is a #GroundVehicle.
3298 * @return #GroundVehicleCache of the vehicle.
3300 const GroundVehicleCache
*Vehicle::GetGroundVehicleCache() const
3302 assert(this->IsGroundVehicle());
3303 if (this->type
== VEH_TRAIN
) {
3304 return &Train::From(this)->gcache
;
3306 return &RoadVehicle::From(this)->gcache
;
3311 * Access the ground vehicle flags of the vehicle.
3312 * @pre The vehicle is a #GroundVehicle.
3313 * @return #GroundVehicleFlags of the vehicle.
3315 uint16
&Vehicle::GetGroundVehicleFlags()
3317 assert(this->IsGroundVehicle());
3318 if (this->type
== VEH_TRAIN
) {
3319 return Train::From(this)->gv_flags
;
3321 return RoadVehicle::From(this)->gv_flags
;
3326 * Access the ground vehicle flags of the vehicle.
3327 * @pre The vehicle is a #GroundVehicle.
3328 * @return #GroundVehicleFlags of the vehicle.
3330 const uint16
&Vehicle::GetGroundVehicleFlags() const
3332 assert(this->IsGroundVehicle());
3333 if (this->type
== VEH_TRAIN
) {
3334 return Train::From(this)->gv_flags
;
3336 return RoadVehicle::From(this)->gv_flags
;
3341 * Calculates the set of vehicles that will be affected by a given selection.
3342 * @param set [inout] Set of affected vehicles.
3343 * @param v First vehicle of the selection.
3344 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
3345 * @pre \a set must be empty.
3346 * @post \a set will contain the vehicles that will be refitted.
3348 void GetVehicleSet(VehicleSet
&set
, Vehicle
*v
, uint8 num_vehicles
)
3350 if (v
->type
== VEH_TRAIN
) {
3351 Train
*u
= Train::From(v
);
3352 /* Only include whole vehicles, so start with the first articulated part */
3353 u
= u
->GetFirstEnginePart();
3355 /* Include num_vehicles vehicles, not counting articulated parts */
3356 for (; u
!= NULL
&& num_vehicles
> 0; num_vehicles
--) {
3358 /* Include current vehicle in the selection. */
3359 set
.Include(u
->index
);
3361 /* If the vehicle is multiheaded, add the other part too. */
3362 if (u
->IsMultiheaded()) set
.Include(u
->other_multiheaded_part
->index
);
3365 } while (u
!= NULL
&& u
->IsArticulatedPart());