1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
8 /** @file vehicle.cpp Base implementations of all vehicles. */
9
10 #include "stdafx.h"
11 #include "error.h"
12 #include "roadveh.h"
13 #include "ship.h"
14 #include "spritecache.h"
15 #include "timetable.h"
16 #include "viewport_func.h"
17 #include "news_func.h"
18 #include "command_func.h"
19 #include "company_func.h"
20 #include "train.h"
21 #include "aircraft.h"
22 #include "newgrf_debug.h"
23 #include "newgrf_sound.h"
24 #include "newgrf_station.h"
25 #include "group_gui.h"
26 #include "strings_func.h"
27 #include "zoom_func.h"
28 #include "date_func.h"
29 #include "vehicle_func.h"
30 #include "autoreplace_func.h"
31 #include "autoreplace_gui.h"
32 #include "station_base.h"
33 #include "ai/ai.hpp"
34 #include "depot_func.h"
35 #include "network/network.h"
36 #include "core/pool_func.hpp"
37 #include "economy_base.h"
38 #include "articulated_vehicles.h"
39 #include "roadstop_base.h"
40 #include "core/random_func.hpp"
41 #include "core/backup_type.hpp"
42 #include "order_backup.h"
43 #include "sound_func.h"
44 #include "effectvehicle_func.h"
45 #include "effectvehicle_base.h"
46 #include "vehiclelist.h"
47 #include "bridge_map.h"
48 #include "tunnel_map.h"
49 #include "depot_map.h"
50 #include "gamelog.h"
51 #include "linkgraph/linkgraph.h"
52 #include "linkgraph/refresh.h"
53 #include "framerate_type.h"
54
55 #include "table/strings.h"
56
57 #include "safeguards.h"
58
59 /* Number of bits in the hash to use from each vehicle coord */
60 static const uint GEN_HASHX_BITS = 6;
61 static const uint GEN_HASHY_BITS = 6;
62
63 /* Size of each hash bucket */
64 static const uint GEN_HASHX_BUCKET_BITS = 7;
65 static const uint GEN_HASHY_BUCKET_BITS = 6;
66
67 /* Compute hash for vehicle coord */
68 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
69 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
70 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
71
72 /* Maximum size until hash repeats */
73 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_LVL_SHIFT);
74 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_LVL_SHIFT);
75
76 /* Increments to reach next bucket in hash table */
77 static const int GEN_HASHX_INC = 1;
78 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
79
80 /* Mask to wrap-around buckets */
81 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
82 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
83
84 VehicleID _new_vehicle_id;
85 uint _returned_refit_capacity; ///< Stores the capacity after a refit operation.
86 uint16 _returned_mail_refit_capacity; ///< Stores the mail capacity after a refit operation (Aircraft only).
87
88
89 /** The pool with all our precious vehicles. */
90 VehiclePool _vehicle_pool("Vehicle");
INSTANTIATE_POOL_METHODS(Vehicle)91 INSTANTIATE_POOL_METHODS(Vehicle)
92
93
94 /**
95 * Determine shared bounds of all sprites.
96 * @param[out] bounds Shared bounds.
97 */
98 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
99 {
100 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
101 for (uint i = 0; i < this->count; ++i) {
102 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
103 if (i == 0) {
104 bounds->left = spr->x_offs;
105 bounds->top = spr->y_offs;
106 bounds->right = spr->width + spr->x_offs - 1;
107 bounds->bottom = spr->height + spr->y_offs - 1;
108 } else {
109 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
110 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
111 int right = spr->width + spr->x_offs - 1;
112 int bottom = spr->height + spr->y_offs - 1;
113 if (right > bounds->right) bounds->right = right;
114 if (bottom > bounds->bottom) bounds->bottom = bottom;
115 }
116 }
117 }
118
119 /**
120 * Draw the sprite sequence.
121 * @param x X position
122 * @param y Y position
123 * @param default_pal Vehicle palette
124 * @param force_pal Whether to ignore individual palettes, and draw everything with \a default_pal.
125 */
Draw(int x,int y,PaletteID default_pal,bool force_pal) const126 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
127 {
128 for (uint i = 0; i < this->count; ++i) {
129 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
130 DrawSprite(this->seq[i].sprite, pal, x, y);
131 }
132 }
133
134 /**
135 * Function to tell if a vehicle needs to be autorenewed
136 * @param *c The vehicle owner
137 * @param use_renew_setting Should the company renew setting be considered?
138 * @return true if the vehicle is old enough for replacement
139 */
NeedsAutorenewing(const Company * c,bool use_renew_setting) const140 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
141 {
142 /* We can always generate the Company pointer when we have the vehicle.
143 * However this takes time and since the Company pointer is often present
144 * when this function is called then it's faster to pass the pointer as an
145 * argument rather than finding it again. */
146 assert(c == Company::Get(this->owner));
147
148 if (use_renew_setting && !c->settings.engine_renew) return false;
149 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
150
151 /* Only engines need renewing */
152 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
153
154 return true;
155 }
156
157 /**
158 * Service a vehicle and all subsequent vehicles in the consist
159 *
160 * @param *v The vehicle or vehicle chain being serviced
161 */
VehicleServiceInDepot(Vehicle * v)162 void VehicleServiceInDepot(Vehicle *v)
163 {
164 assert(v != nullptr);
165 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
166
167 do {
168 v->date_of_last_service = _date;
169 v->breakdowns_since_last_service = 0;
170 v->reliability = v->GetEngine()->reliability;
171 /* Prevent vehicles from breaking down directly after exiting the depot. */
172 v->breakdown_chance /= 4;
173 if (_settings_game.difficulty.vehicle_breakdowns == 1) v->breakdown_chance = 0; // on reduced breakdown
174 v = v->Next();
175 } while (v != nullptr && v->HasEngineType());
176 }
177
178 /**
179 * Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for service or replacement.
180 *
181 * @see NeedsAutomaticServicing()
182 * @return true if the vehicle should go to a depot if a opportunity presents itself.
183 */
NeedsServicing() const184 bool Vehicle::NeedsServicing() const
185 {
186 /* Stopped or crashed vehicles will not move, as such making unmovable
187 * vehicles to go for service is lame. */
188 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
189
190 /* Are we ready for the next service cycle? */
191 const Company *c = Company::Get(this->owner);
192 if (this->ServiceIntervalIsPercent() ?
193 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
194 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
195 return false;
196 }
197
198 /* If we're servicing anyway, because we have not disabled servicing when
199 * there are no breakdowns or we are playing with breakdowns, bail out. */
200 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
201 _settings_game.difficulty.vehicle_breakdowns != 0) {
202 return true;
203 }
204
205 /* Test whether there is some pending autoreplace.
206 * Note: We do this after the service-interval test.
207 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
208 bool pending_replace = false;
209 Money needed_money = c->settings.engine_renew_money;
210 if (needed_money > c->money) return false;
211
212 for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) {
213 bool replace_when_old = false;
214 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
215
216 /* Check engine availability */
217 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
218 /* Is the vehicle old if we are not always replacing? */
219 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
220
221 /* Check refittability */
222 CargoTypes available_cargo_types, union_mask;
223 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
224 /* Is there anything to refit? */
225 if (union_mask != 0) {
226 CargoID cargo_type;
227 /* We cannot refit to mixed cargoes in an automated way */
228 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
229
230 /* Did the old vehicle carry anything? */
231 if (cargo_type != CT_INVALID) {
232 /* We can't refit the vehicle to carry the cargo we want */
233 if (!HasBit(available_cargo_types, cargo_type)) continue;
234 }
235 }
236
237 /* Check money.
238 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
239 pending_replace = true;
240 needed_money += 2 * Engine::Get(new_engine)->GetCost();
241 if (needed_money > c->money) return false;
242 }
243
244 return pending_replace;
245 }
246
247 /**
248 * Checks if the current order should be interrupted for a service-in-depot order.
249 * @see NeedsServicing()
250 * @return true if the current order should be interrupted.
251 */
NeedsAutomaticServicing() const252 bool Vehicle::NeedsAutomaticServicing() const
253 {
254 if (this->HasDepotOrder()) return false;
255 if (this->current_order.IsType(OT_LOADING)) return false;
256 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
257 return NeedsServicing();
258 }
259
Crash(bool flooded)260 uint Vehicle::Crash(bool flooded)
261 {
262 assert((this->vehstatus & VS_CRASHED) == 0);
263 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
264
265 uint pass = 0;
266 /* Stop the vehicle. */
267 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
268 /* crash all wagons, and count passengers */
269 for (Vehicle *v = this; v != nullptr; v = v->Next()) {
270 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
271 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
272 v->vehstatus |= VS_CRASHED;
273 v->MarkAllViewportsDirty();
274 }
275
276 /* Dirty some windows */
277 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
278 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
279 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
280 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
281
282 delete this->cargo_payment;
283 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
284
285 return RandomRange(pass + 1); // Randomise deceased passengers.
286 }
287
288
289 /**
290 * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
291 * @param engine The engine that caused the problem
292 * @param part1 Part 1 of the error message, taking the grfname as parameter 1
293 * @param part2 Part 2 of the error message, taking the engine as parameter 2
294 * @param bug_type Flag to check and set in grfconfig
295 * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
296 */
ShowNewGrfVehicleError(EngineID engine,StringID part1,StringID part2,GRFBugs bug_type,bool critical)297 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
298 {
299 const Engine *e = Engine::Get(engine);
300 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
301
302 /* Missing GRF. Nothing useful can be done in this situation. */
303 if (grfconfig == nullptr) return;
304
305 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
306 SetBit(grfconfig->grf_bugs, bug_type);
307 SetDParamStr(0, grfconfig->GetName());
308 SetDParam(1, engine);
309 ShowErrorMessage(part1, part2, WL_CRITICAL);
310 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
311 }
312
313 /* debug output */
314 char buffer[512];
315
316 SetDParamStr(0, grfconfig->GetName());
317 GetString(buffer, part1, lastof(buffer));
318 Debug(grf, 0, "{}", buffer + 3);
319
320 SetDParam(1, engine);
321 GetString(buffer, part2, lastof(buffer));
322 Debug(grf, 0, "{}", buffer + 3);
323 }
324
325 /**
326 * Logs a bug in GRF and shows a warning message if this
327 * is for the first time this happened.
328 * @param u first vehicle of chain
329 */
VehicleLengthChanged(const Vehicle * u)330 void VehicleLengthChanged(const Vehicle *u)
331 {
332 /* show a warning once for each engine in whole game and once for each GRF after each game load */
333 const Engine *engine = u->GetEngine();
334 uint32 grfid = engine->grf_prop.grffile->grfid;
335 GRFConfig *grfconfig = GetGRFConfig(grfid);
336 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
337 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
338 }
339 }
340
341 /**
342 * Vehicle constructor.
343 * @param type Type of the new vehicle.
344 */
Vehicle(VehicleType type)345 Vehicle::Vehicle(VehicleType type)
346 {
347 this->type = type;
348 this->coord.left = INVALID_COORD;
349 this->sprite_cache.old_coord.left = INVALID_COORD;
350 this->group_id = DEFAULT_GROUP;
351 this->fill_percent_te_id = INVALID_TE_ID;
352 this->first = this;
353 this->colourmap = PAL_NONE;
354 this->cargo_age_counter = 1;
355 this->last_station_visited = INVALID_STATION;
356 this->last_loading_station = INVALID_STATION;
357 }
358
359 /**
360 * Get a value for a vehicle's random_bits.
361 * @return A random value from 0 to 255.
362 */
VehicleRandomBits()363 byte VehicleRandomBits()
364 {
365 return GB(Random(), 0, 8);
366 }
367
368 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
369 * lookup times at the expense of memory usage. */
370 const int HASH_BITS = 7;
371 const int HASH_SIZE = 1 << HASH_BITS;
372 const int HASH_MASK = HASH_SIZE - 1;
373 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
374 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
375
376 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
377 * Profiling results show that 0 is fastest. */
378 const int HASH_RES = 0;
379
380 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
381
VehicleFromTileHash(int xl,int yl,int xu,int yu,void * data,VehicleFromPosProc * proc,bool find_first)382 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
383 {
384 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
385 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
386 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
387 for (; v != nullptr; v = v->hash_tile_next) {
388 Vehicle *a = proc(v, data);
389 if (find_first && a != nullptr) return a;
390 }
391 if (x == xu) break;
392 }
393 if (y == yu) break;
394 }
395
396 return nullptr;
397 }
398
399
400 /**
401 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
402 * @note Do not call this function directly!
403 * @param x The X location on the map
404 * @param y The Y location on the map
405 * @param data Arbitrary data passed to proc
406 * @param proc The proc that determines whether a vehicle will be "found".
407 * @param find_first Whether to return on the first found or iterate over
408 * all vehicles
409 * @return the best matching or first vehicle (depending on find_first).
410 */
VehicleFromPosXY(int x,int y,void * data,VehicleFromPosProc * proc,bool find_first)411 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
412 {
413 const int COLL_DIST = 6;
414
415 /* Hash area to scan is from xl,yl to xu,yu */
416 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
417 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
418 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
419 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
420
421 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
422 }
423
424 /**
425 * Find a vehicle from a specific location. It will call proc for ALL vehicles
426 * on the tile and YOU must make SURE that the "best one" is stored in the
427 * data value and is ALWAYS the same regardless of the order of the vehicles
428 * where proc was called on!
429 * When you fail to do this properly you create an almost untraceable DESYNC!
430 * @note The return value of proc will be ignored.
431 * @note Use this when you have the intention that all vehicles
432 * should be iterated over.
433 * @param x The X location on the map
434 * @param y The Y location on the map
435 * @param data Arbitrary data passed to proc
436 * @param proc The proc that determines whether a vehicle will be "found".
437 */
FindVehicleOnPosXY(int x,int y,void * data,VehicleFromPosProc * proc)438 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
439 {
440 VehicleFromPosXY(x, y, data, proc, false);
441 }
442
443 /**
444 * Checks whether a vehicle in on a specific location. It will call proc for
445 * vehicles until it returns non-nullptr.
446 * @note Use FindVehicleOnPosXY when you have the intention that all vehicles
447 * should be iterated over.
448 * @param x The X location on the map
449 * @param y The Y location on the map
450 * @param data Arbitrary data passed to proc
451 * @param proc The proc that determines whether a vehicle will be "found".
452 * @return True if proc returned non-nullptr.
453 */
HasVehicleOnPosXY(int x,int y,void * data,VehicleFromPosProc * proc)454 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
455 {
456 return VehicleFromPosXY(x, y, data, proc, true) != nullptr;
457 }
458
459 /**
460 * Helper function for FindVehicleOnPos/HasVehicleOnPos.
461 * @note Do not call this function directly!
462 * @param tile The location on the map
463 * @param data Arbitrary data passed to \a proc.
464 * @param proc The proc that determines whether a vehicle will be "found".
465 * @param find_first Whether to return on the first found or iterate over
466 * all vehicles
467 * @return the best matching or first vehicle (depending on find_first).
468 */
VehicleFromPos(TileIndex tile,void * data,VehicleFromPosProc * proc,bool find_first)469 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
470 {
471 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
472 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
473
474 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
475 for (; v != nullptr; v = v->hash_tile_next) {
476 if (v->tile != tile) continue;
477
478 Vehicle *a = proc(v, data);
479 if (find_first && a != nullptr) return a;
480 }
481
482 return nullptr;
483 }
484
485 /**
486 * Find a vehicle from a specific location. It will call \a proc for ALL vehicles
487 * on the tile and YOU must make SURE that the "best one" is stored in the
488 * data value and is ALWAYS the same regardless of the order of the vehicles
489 * where proc was called on!
490 * When you fail to do this properly you create an almost untraceable DESYNC!
491 * @note The return value of \a proc will be ignored.
492 * @note Use this function when you have the intention that all vehicles
493 * should be iterated over.
494 * @param tile The location on the map
495 * @param data Arbitrary data passed to \a proc.
496 * @param proc The proc that determines whether a vehicle will be "found".
497 */
FindVehicleOnPos(TileIndex tile,void * data,VehicleFromPosProc * proc)498 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
499 {
500 VehicleFromPos(tile, data, proc, false);
501 }
502
503 /**
504 * Checks whether a vehicle is on a specific location. It will call \a proc for
505 * vehicles until it returns non-nullptr.
506 * @note Use #FindVehicleOnPos when you have the intention that all vehicles
507 * should be iterated over.
508 * @param tile The location on the map
509 * @param data Arbitrary data passed to \a proc.
510 * @param proc The \a proc that determines whether a vehicle will be "found".
511 * @return True if proc returned non-nullptr.
512 */
HasVehicleOnPos(TileIndex tile,void * data,VehicleFromPosProc * proc)513 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
514 {
515 return VehicleFromPos(tile, data, proc, true) != nullptr;
516 }
517
518 /**
519 * Callback that returns 'real' vehicles lower or at height \c *(int*)data .
520 * @param v Vehicle to examine.
521 * @param data Pointer to height data.
522 * @return \a v if conditions are met, else \c nullptr.
523 */
EnsureNoVehicleProcZ(Vehicle * v,void * data)524 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
525 {
526 int z = *(int*)data;
527
528 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return nullptr;
529 if (v->z_pos > z) return nullptr;
530
531 return v;
532 }
533
534 /**
535 * Ensure there is no vehicle at the ground at the given position.
536 * @param tile Position to examine.
537 * @return Succeeded command (ground is free) or failed command (a vehicle is found).
538 */
EnsureNoVehicleOnGround(TileIndex tile)539 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
540 {
541 int z = GetTileMaxPixelZ(tile);
542
543 /* Value v is not safe in MP games, however, it is used to generate a local
544 * error message only (which may be different for different machines).
545 * Such a message does not affect MP synchronisation.
546 */
547 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
548 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
549 return CommandCost();
550 }
551
552 /** Procedure called for every vehicle found in tunnel/bridge in the hash map */
GetVehicleTunnelBridgeProc(Vehicle * v,void * data)553 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
554 {
555 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return nullptr;
556 if (v == (const Vehicle *)data) return nullptr;
557
558 return v;
559 }
560
561 /**
562 * Finds vehicle in tunnel / bridge
563 * @param tile first end
564 * @param endtile second end
565 * @param ignore Ignore this vehicle when searching
566 * @return Succeeded command (if tunnel/bridge is free) or failed command (if a vehicle is using the tunnel/bridge).
567 */
TunnelBridgeIsFree(TileIndex tile,TileIndex endtile,const Vehicle * ignore)568 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
569 {
570 /* Value v is not safe in MP games, however, it is used to generate a local
571 * error message only (which may be different for different machines).
572 * Such a message does not affect MP synchronisation.
573 */
574 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
575 if (v == nullptr) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
576
577 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
578 return CommandCost();
579 }
580
EnsureNoTrainOnTrackProc(Vehicle * v,void * data)581 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
582 {
583 TrackBits rail_bits = *(TrackBits *)data;
584
585 if (v->type != VEH_TRAIN) return nullptr;
586
587 Train *t = Train::From(v);
588 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return nullptr;
589
590 return v;
591 }
592
593 /**
594 * Tests if a vehicle interacts with the specified track bits.
595 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
596 *
597 * @param tile The tile.
598 * @param track_bits The track bits.
599 * @return \c true if no train that interacts, is found. \c false if a train is found.
600 */
EnsureNoTrainOnTrackBits(TileIndex tile,TrackBits track_bits)601 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
602 {
603 /* Value v is not safe in MP games, however, it is used to generate a local
604 * error message only (which may be different for different machines).
605 * Such a message does not affect MP synchronisation.
606 */
607 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
608 if (v != nullptr) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
609 return CommandCost();
610 }
611
UpdateVehicleTileHash(Vehicle * v,bool remove)612 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
613 {
614 Vehicle **old_hash = v->hash_tile_current;
615 Vehicle **new_hash;
616
617 if (remove) {
618 new_hash = nullptr;
619 } else {
620 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
621 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
622 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
623 }
624
625 if (old_hash == new_hash) return;
626
627 /* Remove from the old position in the hash table */
628 if (old_hash != nullptr) {
629 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
630 *v->hash_tile_prev = v->hash_tile_next;
631 }
632
633 /* Insert vehicle at beginning of the new position in the hash table */
634 if (new_hash != nullptr) {
635 v->hash_tile_next = *new_hash;
636 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
637 v->hash_tile_prev = new_hash;
638 *new_hash = v;
639 }
640
641 /* Remember current hash position */
642 v->hash_tile_current = new_hash;
643 }
644
645 static Vehicle *_vehicle_viewport_hash[1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)];
646
UpdateVehicleViewportHash(Vehicle * v,int x,int y,int old_x,int old_y)647 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y, int old_x, int old_y)
648 {
649 Vehicle **old_hash, **new_hash;
650
651 new_hash = (x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(x, y)];
652 old_hash = (old_x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
653
654 if (old_hash == new_hash) return;
655
656 /* remove from hash table? */
657 if (old_hash != nullptr) {
658 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
659 *v->hash_viewport_prev = v->hash_viewport_next;
660 }
661
662 /* insert into hash table? */
663 if (new_hash != nullptr) {
664 v->hash_viewport_next = *new_hash;
665 if (v->hash_viewport_next != nullptr) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
666 v->hash_viewport_prev = new_hash;
667 *new_hash = v;
668 }
669 }
670
ResetVehicleHash()671 void ResetVehicleHash()
672 {
673 for (Vehicle *v : Vehicle::Iterate()) { v->hash_tile_current = nullptr; }
674 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
675 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
676 }
677
ResetVehicleColourMap()678 void ResetVehicleColourMap()
679 {
680 for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
681 }
682
683 /**
684 * List of vehicles that should check for autoreplace this tick.
685 * Mapping of vehicle -> leave depot immediately after autoreplace.
686 */
687 typedef SmallMap<Vehicle *, bool> AutoreplaceMap;
688 static AutoreplaceMap _vehicles_to_autoreplace;
689
InitializeVehicles()690 void InitializeVehicles()
691 {
692 _vehicles_to_autoreplace.clear();
693 _vehicles_to_autoreplace.shrink_to_fit();
694 ResetVehicleHash();
695 }
696
CountVehiclesInChain(const Vehicle * v)697 uint CountVehiclesInChain(const Vehicle *v)
698 {
699 uint count = 0;
700 do count++; while ((v = v->Next()) != nullptr);
701 return count;
702 }
703
704 /**
705 * Check if a vehicle is counted in num_engines in each company struct
706 * @return true if the vehicle is counted in num_engines
707 */
IsEngineCountable() const708 bool Vehicle::IsEngineCountable() const
709 {
710 switch (this->type) {
711 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
712 case VEH_TRAIN:
713 return !this->IsArticulatedPart() && // tenders and other articulated parts
714 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
715 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
716 case VEH_SHIP: return true;
717 default: return false; // Only count company buildable vehicles
718 }
719 }
720
721 /**
722 * Check whether Vehicle::engine_type has any meaning.
723 * @return true if the vehicle has a usable engine type.
724 */
HasEngineType() const725 bool Vehicle::HasEngineType() const
726 {
727 switch (this->type) {
728 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
729 case VEH_TRAIN:
730 case VEH_ROAD:
731 case VEH_SHIP: return true;
732 default: return false;
733 }
734 }
735
736 /**
737 * Retrieves the engine of the vehicle.
738 * @return Engine of the vehicle.
739 * @pre HasEngineType() == true
740 */
GetEngine() const741 const Engine *Vehicle::GetEngine() const
742 {
743 return Engine::Get(this->engine_type);
744 }
745
746 /**
747 * Retrieve the NewGRF the vehicle is tied to.
748 * This is the GRF providing the Action 3 for the engine type.
749 * @return NewGRF associated to the vehicle.
750 */
GetGRF() const751 const GRFFile *Vehicle::GetGRF() const
752 {
753 return this->GetEngine()->GetGRF();
754 }
755
756 /**
757 * Retrieve the GRF ID of the NewGRF the vehicle is tied to.
758 * This is the GRF providing the Action 3 for the engine type.
759 * @return GRF ID of the associated NewGRF.
760 */
GetGRFID() const761 uint32 Vehicle::GetGRFID() const
762 {
763 return this->GetEngine()->GetGRFID();
764 }
765
766 /**
767 * Handle the pathfinding result, especially the lost status.
768 * If the vehicle is now lost and wasn't previously fire an
769 * event to the AIs and a news message to the user. If the
770 * vehicle is not lost anymore remove the news message.
771 * @param path_found Whether the vehicle has a path to its destination.
772 */
HandlePathfindingResult(bool path_found)773 void Vehicle::HandlePathfindingResult(bool path_found)
774 {
775 if (path_found) {
776 /* Route found, is the vehicle marked with "lost" flag? */
777 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
778
779 /* Clear the flag as the PF's problem was solved. */
780 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
781 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
782 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
783 /* Delete the news item. */
784 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
785 return;
786 }
787
788 /* Were we already lost? */
789 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
790
791 /* It is first time the problem occurred, set the "lost" flag. */
792 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
793 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
794 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
795 /* Notify user about the event. */
796 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
797 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
798 SetDParam(0, this->index);
799 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
800 }
801 }
802
803 /** Destroy all stuff that (still) needs the virtual functions to work properly */
PreDestructor()804 void Vehicle::PreDestructor()
805 {
806 if (CleaningPool()) return;
807
808 if (Station::IsValidID(this->last_station_visited)) {
809 Station *st = Station::Get(this->last_station_visited);
810 st->loading_vehicles.remove(this);
811
812 HideFillingPercent(&this->fill_percent_te_id);
813 this->CancelReservation(INVALID_STATION, st);
814 delete this->cargo_payment;
815 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
816 }
817
818 if (this->IsEngineCountable()) {
819 GroupStatistics::CountEngine(this, -1);
820 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
821 GroupStatistics::UpdateAutoreplace(this->owner);
822
823 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
824 DeleteGroupHighlightOfVehicle(this);
825 }
826
827 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
828 Aircraft *a = Aircraft::From(this);
829 Station *st = GetTargetAirportIfValid(a);
830 if (st != nullptr) {
831 const AirportFTA *layout = st->airport.GetFTA()->layout;
832 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
833 }
834 }
835
836
837 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
838 RoadVehicle *v = RoadVehicle::From(this);
839 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
840 /* Leave the drive through roadstop, when you have not already left it. */
841 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
842 }
843 }
844
845 if (this->Previous() == nullptr) {
846 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
847 }
848
849 if (this->IsPrimaryVehicle()) {
850 CloseWindowById(WC_VEHICLE_VIEW, this->index);
851 CloseWindowById(WC_VEHICLE_ORDERS, this->index);
852 CloseWindowById(WC_VEHICLE_REFIT, this->index);
853 CloseWindowById(WC_VEHICLE_DETAILS, this->index);
854 CloseWindowById(WC_VEHICLE_TIMETABLE, this->index);
855 SetWindowDirty(WC_COMPANY, this->owner);
856 OrderBackup::ClearVehicle(this);
857 }
858 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
859
860 this->cargo.Truncate();
861 DeleteVehicleOrders(this);
862 DeleteDepotHighlightOfVehicle(this);
863
864 extern void StopGlobalFollowVehicle(const Vehicle *v);
865 StopGlobalFollowVehicle(this);
866
867 ReleaseDisastersTargetingVehicle(this->index);
868 }
869
~Vehicle()870 Vehicle::~Vehicle()
871 {
872 if (CleaningPool()) {
873 this->cargo.OnCleanPool();
874 return;
875 }
876
877 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
878 * it may happen that vehicle chain is deleted when visible */
879 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
880
881 Vehicle *v = this->Next();
882 this->SetNext(nullptr);
883
884 delete v;
885
886 UpdateVehicleTileHash(this, true);
887 UpdateVehicleViewportHash(this, INVALID_COORD, 0, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
888 DeleteVehicleNews(this->index, INVALID_STRING_ID);
889 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
890 }
891
892 /**
893 * Adds a vehicle to the list of vehicles that visited a depot this tick
894 * @param *v vehicle to add
895 */
VehicleEnteredDepotThisTick(Vehicle * v)896 void VehicleEnteredDepotThisTick(Vehicle *v)
897 {
898 /* Vehicle should stop in the depot if it was in 'stopping' state */
899 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
900
901 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
902 * stopping in the depot, so we stop it to ensure that it will not reserve
903 * the path out of the depot before we might autoreplace it to a different
904 * engine. The new engine would not own the reserved path we store that we
905 * stopped the vehicle, so autoreplace can start it again */
906 v->vehstatus |= VS_STOPPED;
907 }
908
909 /**
910 * Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
911 * Each tick, it processes vehicles with "index % DAY_TICKS == _date_fract",
912 * so each day, all vehicles are processes in DAY_TICKS steps.
913 */
RunVehicleDayProc()914 static void RunVehicleDayProc()
915 {
916 if (_game_mode != GM_NORMAL) return;
917
918 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
919 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
920 Vehicle *v = Vehicle::Get(i);
921 if (v == nullptr) continue;
922
923 /* Call the 32-day callback if needed */
924 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
925 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
926 if (callback != CALLBACK_FAILED) {
927 if (HasBit(callback, 0)) {
928 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
929 }
930
931 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
932 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
933 if (callback != 0) v->First()->MarkDirty();
934
935 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
936 }
937 }
938
939 /* This is called once per day for each vehicle, but not in the first tick of the day */
940 v->OnNewDay();
941 }
942 }
943
CallVehicleTicks()944 void CallVehicleTicks()
945 {
946 _vehicles_to_autoreplace.clear();
947
948 RunVehicleDayProc();
949
950 {
951 PerformanceMeasurer framerate(PFE_GL_ECONOMY);
952 for (Station *st : Station::Iterate()) LoadUnloadStation(st);
953 }
954 PerformanceAccumulator::Reset(PFE_GL_TRAINS);
955 PerformanceAccumulator::Reset(PFE_GL_ROADVEHS);
956 PerformanceAccumulator::Reset(PFE_GL_SHIPS);
957 PerformanceAccumulator::Reset(PFE_GL_AIRCRAFT);
958
959 for (Vehicle *v : Vehicle::Iterate()) {
960 [[maybe_unused]] size_t vehicle_index = v->index;
961
962 /* Vehicle could be deleted in this tick */
963 if (!v->Tick()) {
964 assert(Vehicle::Get(vehicle_index) == nullptr);
965 continue;
966 }
967
968 assert(Vehicle::Get(vehicle_index) == v);
969
970 switch (v->type) {
971 default: break;
972
973 case VEH_TRAIN:
974 case VEH_ROAD:
975 case VEH_AIRCRAFT:
976 case VEH_SHIP: {
977 Vehicle *front = v->First();
978
979 if (v->vcache.cached_cargo_age_period != 0) {
980 v->cargo_age_counter = std::min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
981 if (--v->cargo_age_counter == 0) {
982 v->cargo.AgeCargo();
983 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
984 }
985 }
986
987 /* Do not play any sound when crashed */
988 if (front->vehstatus & VS_CRASHED) continue;
989
990 /* Do not play any sound when in depot or tunnel */
991 if (v->vehstatus & VS_HIDDEN) continue;
992
993 /* Do not play any sound when stopped */
994 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
995
996 /* Check vehicle type specifics */
997 switch (v->type) {
998 case VEH_TRAIN:
999 if (Train::From(v)->IsWagon()) continue;
1000 break;
1001
1002 case VEH_ROAD:
1003 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1004 break;
1005
1006 case VEH_AIRCRAFT:
1007 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1008 break;
1009
1010 default:
1011 break;
1012 }
1013
1014 v->motion_counter += front->cur_speed;
1015 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1016 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1017
1018 /* Play an alternating running sound every 16 ticks */
1019 if (GB(v->tick_counter, 0, 4) == 0) {
1020 /* Play running sound when speed > 0 and not braking */
1021 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1022 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1023 }
1024
1025 break;
1026 }
1027 }
1028 }
1029
1030 Backup<CompanyID> cur_company(_current_company, FILE_LINE);
1031 for (auto &it : _vehicles_to_autoreplace) {
1032 Vehicle *v = it.first;
1033 /* Autoreplace needs the current company set as the vehicle owner */
1034 cur_company.Change(v->owner);
1035
1036 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1037 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1038 * they are already leaving the depot again before being replaced. */
1039 if (it.second) v->vehstatus &= ~VS_STOPPED;
1040
1041 /* Store the position of the effect as the vehicle pointer will become invalid later */
1042 int x = v->x_pos;
1043 int y = v->y_pos;
1044 int z = v->z_pos;
1045
1046 const Company *c = Company::Get(_current_company);
1047 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1048 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1049 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1050
1051 if (!IsLocalCompany()) continue;
1052
1053 if (res.Succeeded() && res.GetCost() != 0) {
1054 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1055 continue;
1056 }
1057
1058 StringID error_message = res.GetErrorMessage();
1059 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1060
1061 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1062
1063 StringID message;
1064 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1065 message = error_message;
1066 } else {
1067 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1068 }
1069
1070 SetDParam(0, v->index);
1071 SetDParam(1, error_message);
1072 AddVehicleAdviceNewsItem(message, v->index);
1073 }
1074
1075 cur_company.Restore();
1076 }
1077
1078 /**
1079 * Add vehicle sprite for drawing to the screen.
1080 * @param v Vehicle to draw.
1081 */
DoDrawVehicle(const Vehicle * v)1082 static void DoDrawVehicle(const Vehicle *v)
1083 {
1084 PaletteID pal = PAL_NONE;
1085
1086 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1087
1088 /* Check whether the vehicle shall be transparent due to the game state */
1089 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1090
1091 if (v->type == VEH_EFFECT) {
1092 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1093 * However, transparent smoke and bubbles look weird, so always hide them. */
1094 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1095 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1096 }
1097
1098 StartSpriteCombine();
1099 for (uint i = 0; i < v->sprite_cache.sprite_seq.count; ++i) {
1100 PaletteID pal2 = v->sprite_cache.sprite_seq.seq[i].pal;
1101 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1102 AddSortableSpriteToDraw(v->sprite_cache.sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1103 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1104 }
1105 EndSpriteCombine();
1106 }
1107
1108 /**
1109 * Add the vehicle sprites that should be drawn at a part of the screen.
1110 * @param dpi Rectangle being drawn.
1111 */
ViewportAddVehicles(DrawPixelInfo * dpi)1112 void ViewportAddVehicles(DrawPixelInfo *dpi)
1113 {
1114 /* The bounding rectangle */
1115 const int l = dpi->left;
1116 const int r = dpi->left + dpi->width;
1117 const int t = dpi->top;
1118 const int b = dpi->top + dpi->height;
1119
1120 /* Border size of MAX_VEHICLE_PIXEL_xy */
1121 const int xb = MAX_VEHICLE_PIXEL_X * ZOOM_LVL_BASE;
1122 const int yb = MAX_VEHICLE_PIXEL_Y * ZOOM_LVL_BASE;
1123
1124 /* The hash area to scan */
1125 int xl, xu, yl, yu;
1126
1127 if (dpi->width + xb < GEN_HASHX_SIZE) {
1128 xl = GEN_HASHX(l - xb);
1129 xu = GEN_HASHX(r);
1130 } else {
1131 /* scan whole hash row */
1132 xl = 0;
1133 xu = GEN_HASHX_MASK;
1134 }
1135
1136 if (dpi->height + yb < GEN_HASHY_SIZE) {
1137 yl = GEN_HASHY(t - yb);
1138 yu = GEN_HASHY(b);
1139 } else {
1140 /* scan whole column */
1141 yl = 0;
1142 yu = GEN_HASHY_MASK;
1143 }
1144
1145 for (int y = yl;; y = (y + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1146 for (int x = xl;; x = (x + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1147 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1148
1149 while (v != nullptr) {
1150
1151 if (!(v->vehstatus & VS_HIDDEN) &&
1152 l <= v->coord.right + xb &&
1153 t <= v->coord.bottom + yb &&
1154 r >= v->coord.left - xb &&
1155 b >= v->coord.top - yb)
1156 {
1157 /*
1158 * This vehicle can potentially be drawn as part of this viewport and
1159 * needs to be revalidated, as the sprite may not be correct.
1160 */
1161 if (v->sprite_cache.revalidate_before_draw) {
1162 VehicleSpriteSeq seq;
1163 v->GetImage(v->direction, EIT_ON_MAP, &seq);
1164
1165 if (seq.IsValid() && v->sprite_cache.sprite_seq != seq) {
1166 v->sprite_cache.sprite_seq = seq;
1167 /*
1168 * A sprite change may also result in a bounding box change,
1169 * so we need to update the bounding box again before we
1170 * check to see if the vehicle should be drawn. Note that
1171 * we can't interfere with the viewport hash at this point,
1172 * so we keep the original hash on the assumption there will
1173 * not be a significant change in the top and left coordinates
1174 * of the vehicle.
1175 */
1176 v->UpdateBoundingBoxCoordinates(false);
1177
1178 }
1179
1180 v->sprite_cache.revalidate_before_draw = false;
1181 }
1182
1183 if (l <= v->coord.right &&
1184 t <= v->coord.bottom &&
1185 r >= v->coord.left &&
1186 b >= v->coord.top) DoDrawVehicle(v);
1187 }
1188
1189 v = v->hash_viewport_next;
1190 }
1191
1192 if (x == xu) break;
1193 }
1194
1195 if (y == yu) break;
1196 }
1197 }
1198
1199 /**
1200 * Find the vehicle close to the clicked coordinates.
1201 * @param vp Viewport clicked in.
1202 * @param x X coordinate in the viewport.
1203 * @param y Y coordinate in the viewport.
1204 * @return Closest vehicle, or \c nullptr if none found.
1205 */
CheckClickOnVehicle(const Viewport * vp,int x,int y)1206 Vehicle *CheckClickOnVehicle(const Viewport *vp, int x, int y)
1207 {
1208 Vehicle *found = nullptr;
1209 uint dist, best_dist = UINT_MAX;
1210
1211 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return nullptr;
1212
1213 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1214 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1215
1216 for (Vehicle *v : Vehicle::Iterate()) {
1217 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1218 x >= v->coord.left && x <= v->coord.right &&
1219 y >= v->coord.top && y <= v->coord.bottom) {
1220
1221 dist = std::max(
1222 abs(((v->coord.left + v->coord.right) >> 1) - x),
1223 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1224 );
1225
1226 if (dist < best_dist) {
1227 found = v;
1228 best_dist = dist;
1229 }
1230 }
1231 }
1232
1233 return found;
1234 }
1235
1236 /**
1237 * Decrease the value of a vehicle.
1238 * @param v %Vehicle to devaluate.
1239 */
DecreaseVehicleValue(Vehicle * v)1240 void DecreaseVehicleValue(Vehicle *v)
1241 {
1242 v->value -= v->value >> 8;
1243 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1244 }
1245
1246 static const byte _breakdown_chance[64] = {
1247 3, 3, 3, 3, 3, 3, 3, 3,
1248 4, 4, 5, 5, 6, 6, 7, 7,
1249 8, 8, 9, 9, 10, 10, 11, 11,
1250 12, 13, 13, 13, 13, 14, 15, 16,
1251 17, 19, 21, 25, 28, 31, 34, 37,
1252 40, 44, 48, 52, 56, 60, 64, 68,
1253 72, 80, 90, 100, 110, 120, 130, 140,
1254 150, 170, 190, 210, 230, 250, 250, 250,
1255 };
1256
CheckVehicleBreakdown(Vehicle * v)1257 void CheckVehicleBreakdown(Vehicle *v)
1258 {
1259 int rel, rel_old;
1260
1261 /* decrease reliability */
1262 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
1263 _settings_game.difficulty.vehicle_breakdowns != 0) {
1264 v->reliability = rel = std::max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1265 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1266 }
1267
1268 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1269 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1270 v->cur_speed < 5 || _game_mode == GM_MENU) {
1271 return;
1272 }
1273
1274 uint32 r = Random();
1275
1276 /* increase chance of failure */
1277 int chance = v->breakdown_chance + 1;
1278 if (Chance16I(1, 25, r)) chance += 25;
1279 v->breakdown_chance = std::min(255, chance);
1280
1281 /* calculate reliability value to use in comparison */
1282 rel = v->reliability;
1283 if (v->type == VEH_SHIP) rel += 0x6666;
1284
1285 /* reduced breakdowns? */
1286 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1287
1288 /* check if to break down */
1289 if (_breakdown_chance[(uint)std::min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1290 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1291 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1292 v->breakdown_chance = 0;
1293 }
1294 }
1295
1296 /**
1297 * Handle all of the aspects of a vehicle breakdown
1298 * This includes adding smoke and sounds, and ending the breakdown when appropriate.
1299 * @return true iff the vehicle is stopped because of a breakdown
1300 * @note This function always returns false for aircraft, since these never stop for breakdowns
1301 */
HandleBreakdown()1302 bool Vehicle::HandleBreakdown()
1303 {
1304 /* Possible states for Vehicle::breakdown_ctr
1305 * 0 - vehicle is running normally
1306 * 1 - vehicle is currently broken down
1307 * 2 - vehicle is going to break down now
1308 * >2 - vehicle is counting down to the actual breakdown event */
1309 switch (this->breakdown_ctr) {
1310 case 0:
1311 return false;
1312
1313 case 2:
1314 this->breakdown_ctr = 1;
1315
1316 if (this->breakdowns_since_last_service != 255) {
1317 this->breakdowns_since_last_service++;
1318 }
1319
1320 if (this->type == VEH_AIRCRAFT) {
1321 /* Aircraft just need this flag, the rest is handled elsewhere */
1322 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1323 } else {
1324 this->cur_speed = 0;
1325
1326 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1327 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1328 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1329 (train_or_ship ? SND_10_BREAKDOWN_TRAIN_SHIP : SND_0F_BREAKDOWN_ROADVEHICLE) :
1330 (train_or_ship ? SND_3A_BREAKDOWN_TRAIN_SHIP_TOYLAND : SND_35_BREAKDOWN_ROADVEHICLE_TOYLAND), this);
1331 }
1332
1333 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1334 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1335 if (u != nullptr) u->animation_state = this->breakdown_delay * 2;
1336 }
1337 }
1338
1339 this->MarkDirty(); // Update graphics after speed is zeroed
1340 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1341 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1342
1343 FALLTHROUGH;
1344 case 1:
1345 /* Aircraft breakdowns end only when arriving at the airport */
1346 if (this->type == VEH_AIRCRAFT) return false;
1347
1348 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1349 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1350 if (--this->breakdown_delay == 0) {
1351 this->breakdown_ctr = 0;
1352 this->MarkDirty();
1353 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1354 }
1355 }
1356 return true;
1357
1358 default:
1359 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1360 return false;
1361 }
1362 }
1363
1364 /**
1365 * Update age of a vehicle.
1366 * @param v Vehicle to update.
1367 */
AgeVehicle(Vehicle * v)1368 void AgeVehicle(Vehicle *v)
1369 {
1370 if (v->age < MAX_DAY) {
1371 v->age++;
1372 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1373 }
1374
1375 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1376
1377 int age = v->age - v->max_age;
1378 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1379 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1380 v->reliability_spd_dec <<= 1;
1381 }
1382
1383 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1384
1385 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1386 if (v->Previous() != nullptr || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1387
1388 const Company *c = Company::Get(v->owner);
1389 /* Don't warn if a renew is active */
1390 if (c->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1391 /* Don't warn if a replacement is active */
1392 if (EngineHasReplacementForCompany(c, v->engine_type, v->group_id)) return;
1393
1394 StringID str;
1395 if (age == -DAYS_IN_LEAP_YEAR) {
1396 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1397 } else if (age == 0) {
1398 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1399 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1400 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1401 } else {
1402 return;
1403 }
1404
1405 SetDParam(0, v->index);
1406 AddVehicleAdviceNewsItem(str, v->index);
1407 }
1408
1409 /**
1410 * Calculates how full a vehicle is.
1411 * @param front The front vehicle of the consist to check.
1412 * @param colour The string to show depending on if we are unloading or loading
1413 * @return A percentage of how full the Vehicle is.
1414 * Percentages are rounded towards 50%, so that 0% and 100% are only returned
1415 * if the vehicle is completely empty or full.
1416 * This is useful for both display and conditional orders.
1417 */
CalcPercentVehicleFilled(const Vehicle * front,StringID * colour)1418 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1419 {
1420 int count = 0;
1421 int max = 0;
1422 int cars = 0;
1423 int unloading = 0;
1424 bool loading = false;
1425
1426 bool is_loading = front->current_order.IsType(OT_LOADING);
1427
1428 /* The station may be nullptr when the (colour) string does not need to be set. */
1429 const Station *st = Station::GetIfValid(front->last_station_visited);
1430 assert(colour == nullptr || (st != nullptr && is_loading));
1431
1432 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1433 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1434
1435 /* Count up max and used */
1436 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
1437 count += v->cargo.StoredCount();
1438 max += v->cargo_cap;
1439 if (v->cargo_cap != 0 && colour != nullptr) {
1440 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1441 loading |= !order_no_load &&
1442 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1443 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1444 cars++;
1445 }
1446 }
1447
1448 if (colour != nullptr) {
1449 if (unloading == 0 && loading) {
1450 *colour = STR_PERCENT_UP;
1451 } else if (unloading == 0 && !loading) {
1452 *colour = STR_PERCENT_NONE;
1453 } else if (cars == unloading || !loading) {
1454 *colour = STR_PERCENT_DOWN;
1455 } else {
1456 *colour = STR_PERCENT_UP_DOWN;
1457 }
1458 }
1459
1460 /* Train without capacity */
1461 if (max == 0) return 100;
1462
1463 /* Return the percentage */
1464 if (count * 2 < max) {
1465 /* Less than 50%; round up, so that 0% means really empty. */
1466 return CeilDiv(count * 100, max);
1467 } else {
1468 /* More than 50%; round down, so that 100% means really full. */
1469 return (count * 100) / max;
1470 }
1471 }
1472
1473 /**
1474 * Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it, etc.
1475 * @param v Vehicle that entered a depot.
1476 */
VehicleEnterDepot(Vehicle * v)1477 void VehicleEnterDepot(Vehicle *v)
1478 {
1479 /* Always work with the front of the vehicle */
1480 assert(v == v->First());
1481
1482 switch (v->type) {
1483 case VEH_TRAIN: {
1484 Train *t = Train::From(v);
1485 SetWindowClassesDirty(WC_TRAINS_LIST);
1486 /* Clear path reservation */
1487 SetDepotReservation(t->tile, false);
1488 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1489
1490 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1491 t->wait_counter = 0;
1492 t->force_proceed = TFP_NONE;
1493 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1494 t->ConsistChanged(CCF_ARRANGE);
1495 break;
1496 }
1497
1498 case VEH_ROAD:
1499 SetWindowClassesDirty(WC_ROADVEH_LIST);
1500 break;
1501
1502 case VEH_SHIP: {
1503 SetWindowClassesDirty(WC_SHIPS_LIST);
1504 Ship *ship = Ship::From(v);
1505 ship->state = TRACK_BIT_DEPOT;
1506 ship->UpdateCache();
1507 ship->UpdateViewport(true, true);
1508 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1509 break;
1510 }
1511
1512 case VEH_AIRCRAFT:
1513 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1514 HandleAircraftEnterHangar(Aircraft::From(v));
1515 break;
1516 default: NOT_REACHED();
1517 }
1518 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1519
1520 if (v->type != VEH_TRAIN) {
1521 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1522 * 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 */
1523 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1524 }
1525 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1526
1527 v->vehstatus |= VS_HIDDEN;
1528 v->cur_speed = 0;
1529
1530 VehicleServiceInDepot(v);
1531
1532 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1533 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1534 v->MarkDirty();
1535
1536 InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
1537
1538 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1539 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1540
1541 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1542
1543 /* Test whether we are heading for this depot. If not, do nothing.
1544 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1545 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1546 real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1547 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1548 /* We are heading for another depot, keep driving. */
1549 return;
1550 }
1551
1552 if (v->current_order.IsRefit()) {
1553 Backup<CompanyID> cur_company(_current_company, v->owner, FILE_LINE);
1554 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1555 cur_company.Restore();
1556
1557 if (cost.Failed()) {
1558 _vehicles_to_autoreplace[v] = false;
1559 if (v->owner == _local_company) {
1560 /* Notify the user that we stopped the vehicle */
1561 SetDParam(0, v->index);
1562 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1563 }
1564 } else if (cost.GetCost() != 0) {
1565 v->profit_this_year -= cost.GetCost() << 8;
1566 if (v->owner == _local_company) {
1567 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1568 }
1569 }
1570 }
1571
1572 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1573 /* Part of orders */
1574 v->DeleteUnreachedImplicitOrders();
1575 UpdateVehicleTimetable(v, true);
1576 v->IncrementImplicitOrderIndex();
1577 }
1578 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1579 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1580 _vehicles_to_autoreplace[v] = false;
1581 /* Invalidate last_loading_station. As the link from the station
1582 * before the stop to the station after the stop can't be predicted
1583 * we shouldn't construct it when the vehicle visits the next stop. */
1584 v->last_loading_station = INVALID_STATION;
1585 if (v->owner == _local_company) {
1586 SetDParam(0, v->index);
1587 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1588 }
1589 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1590 }
1591 v->current_order.MakeDummy();
1592 }
1593 }
1594
1595
1596 /**
1597 * Update the position of the vehicle. This will update the hash that tells
1598 * which vehicles are on a tile.
1599 */
UpdatePosition()1600 void Vehicle::UpdatePosition()
1601 {
1602 UpdateVehicleTileHash(this, false);
1603 }
1604
1605 /**
1606 * Update the bounding box co-ordinates of the vehicle
1607 * @param update_cache Update the cached values for previous co-ordinate values
1608 */
UpdateBoundingBoxCoordinates(bool update_cache) const1609 void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache) const
1610 {
1611 Rect new_coord;
1612 this->sprite_cache.sprite_seq.GetBounds(&new_coord);
1613
1614 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1615 new_coord.left += pt.x;
1616 new_coord.top += pt.y;
1617 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1618 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1619
1620 if (update_cache) {
1621 /*
1622 * If the old coordinates are invalid, set the cache to the new coordinates for correct
1623 * behaviour the next time the coordinate cache is checked.
1624 */
1625 this->sprite_cache.old_coord = this->coord.left == INVALID_COORD ? new_coord : this->coord;
1626 }
1627 else {
1628 /* Extend the bounds of the existing cached bounding box so the next dirty window is correct */
1629 this->sprite_cache.old_coord.left = std::min(this->sprite_cache.old_coord.left, this->coord.left);
1630 this->sprite_cache.old_coord.top = std::min(this->sprite_cache.old_coord.top, this->coord.top);
1631 this->sprite_cache.old_coord.right = std::max(this->sprite_cache.old_coord.right, this->coord.right);
1632 this->sprite_cache.old_coord.bottom = std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom);
1633 }
1634
1635 this->coord = new_coord;
1636 }
1637
1638 /**
1639 * Update the vehicle on the viewport, updating the right hash and setting the new coordinates.
1640 * @param dirty Mark the (new and old) coordinates of the vehicle as dirty.
1641 */
UpdateViewport(bool dirty)1642 void Vehicle::UpdateViewport(bool dirty)
1643 {
1644 /* If the existing cache is invalid we should ignore it, as it will be set to the current coords by UpdateBoundingBoxCoordinates */
1645 bool ignore_cached_coords = this->sprite_cache.old_coord.left == INVALID_COORD;
1646
1647 this->UpdateBoundingBoxCoordinates(true);
1648
1649 if (ignore_cached_coords) {
1650 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, INVALID_COORD, INVALID_COORD);
1651 } else {
1652 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
1653 }
1654
1655 if (dirty) {
1656 if (ignore_cached_coords) {
1657 this->sprite_cache.is_viewport_candidate = this->MarkAllViewportsDirty();
1658 } else {
1659 this->sprite_cache.is_viewport_candidate = ::MarkAllViewportsDirty(
1660 std::min(this->sprite_cache.old_coord.left, this->coord.left),
1661 std::min(this->sprite_cache.old_coord.top, this->coord.top),
1662 std::max(this->sprite_cache.old_coord.right, this->coord.right),
1663 std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom));
1664 }
1665 }
1666 }
1667
1668 /**
1669 * Update the position of the vehicle, and update the viewport.
1670 */
UpdatePositionAndViewport()1671 void Vehicle::UpdatePositionAndViewport()
1672 {
1673 this->UpdatePosition();
1674 this->UpdateViewport(true);
1675 }
1676
1677 /**
1678 * Marks viewports dirty where the vehicle's image is.
1679 * @return true if at least one viewport has a dirty block
1680 */
MarkAllViewportsDirty() const1681 bool Vehicle::MarkAllViewportsDirty() const
1682 {
1683 return ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1684 }
1685
1686 /**
1687 * Get position information of a vehicle when moving one pixel in the direction it is facing
1688 * @param v Vehicle to move
1689 * @return Position information after the move
1690 */
GetNewVehiclePos(const Vehicle * v)1691 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1692 {
1693 static const int8 _delta_coord[16] = {
1694 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1695 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1696 };
1697
1698 int x = v->x_pos + _delta_coord[v->direction];
1699 int y = v->y_pos + _delta_coord[v->direction + 8];
1700
1701 GetNewVehiclePosResult gp;
1702 gp.x = x;
1703 gp.y = y;
1704 gp.old_tile = v->tile;
1705 gp.new_tile = TileVirtXY(x, y);
1706 return gp;
1707 }
1708
1709 static const Direction _new_direction_table[] = {
1710 DIR_N, DIR_NW, DIR_W,
1711 DIR_NE, DIR_SE, DIR_SW,
1712 DIR_E, DIR_SE, DIR_S
1713 };
1714
GetDirectionTowards(const Vehicle * v,int x,int y)1715 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1716 {
1717 int i = 0;
1718
1719 if (y >= v->y_pos) {
1720 if (y != v->y_pos) i += 3;
1721 i += 3;
1722 }
1723
1724 if (x >= v->x_pos) {
1725 if (x != v->x_pos) i++;
1726 i++;
1727 }
1728
1729 Direction dir = v->direction;
1730
1731 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1732 if (dirdiff == DIRDIFF_SAME) return dir;
1733 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1734 }
1735
1736 /**
1737 * Call the tile callback function for a vehicle entering a tile
1738 * @param v Vehicle entering the tile
1739 * @param tile Tile entered
1740 * @param x X position
1741 * @param y Y position
1742 * @return Some meta-data over the to be entered tile.
1743 * @see VehicleEnterTileStatus to see what the bits in the return value mean.
1744 */
VehicleEnterTile(Vehicle * v,TileIndex tile,int x,int y)1745 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1746 {
1747 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1748 }
1749
1750 /**
1751 * Initializes the structure. Vehicle unit numbers are supposed not to change after
1752 * struct initialization, except after each call to this->NextID() the returned value
1753 * is assigned to a vehicle.
1754 * @param type type of vehicle
1755 * @param owner owner of vehicles
1756 */
FreeUnitIDGenerator(VehicleType type,CompanyID owner)1757 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(nullptr), maxid(0), curid(0)
1758 {
1759 /* Find maximum */
1760 for (const Vehicle *v : Vehicle::Iterate()) {
1761 if (v->type == type && v->owner == owner) {
1762 this->maxid = std::max<UnitID>(this->maxid, v->unitnumber);
1763 }
1764 }
1765
1766 if (this->maxid == 0) return;
1767
1768 /* Reserving 'maxid + 2' because we need:
1769 * - space for the last item (with v->unitnumber == maxid)
1770 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1771 this->cache = CallocT<bool>(this->maxid + 2);
1772
1773 /* Fill the cache */
1774 for (const Vehicle *v : Vehicle::Iterate()) {
1775 if (v->type == type && v->owner == owner) {
1776 this->cache[v->unitnumber] = true;
1777 }
1778 }
1779 }
1780
1781 /** Returns next free UnitID. Supposes the last returned value was assigned to a vehicle. */
NextID()1782 UnitID FreeUnitIDGenerator::NextID()
1783 {
1784 if (this->maxid <= this->curid) return ++this->curid;
1785
1786 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1787
1788 return this->curid;
1789 }
1790
1791 /**
1792 * Get an unused unit number for a vehicle (if allowed).
1793 * @param type Type of vehicle
1794 * @return A unused unit number for the given type of vehicle if it is allowed to build one, else \c UINT16_MAX.
1795 */
GetFreeUnitNumber(VehicleType type)1796 UnitID GetFreeUnitNumber(VehicleType type)
1797 {
1798 /* Check whether it is allowed to build another vehicle. */
1799 uint max_veh;
1800 switch (type) {
1801 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1802 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1803 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1804 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1805 default: NOT_REACHED();
1806 }
1807
1808 const Company *c = Company::Get(_current_company);
1809 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1810
1811 FreeUnitIDGenerator gen(type, _current_company);
1812
1813 return gen.NextID();
1814 }
1815
1816
1817 /**
1818 * Check whether we can build infrastructure for the given
1819 * vehicle type. This to disable building stations etc. when
1820 * you are not allowed/able to have the vehicle type yet.
1821 * @param type the vehicle type to check this for
1822 * @return true if there is any reason why you may build
1823 * the infrastructure for the given vehicle type
1824 */
CanBuildVehicleInfrastructure(VehicleType type,byte subtype)1825 bool CanBuildVehicleInfrastructure(VehicleType type, byte subtype)
1826 {
1827 assert(IsCompanyBuildableVehicleType(type));
1828
1829 if (!Company::IsValidID(_local_company)) return false;
1830
1831 UnitID max;
1832 switch (type) {
1833 case VEH_TRAIN:
1834 if (!HasAnyRailtypesAvail(_local_company)) return false;
1835 max = _settings_game.vehicle.max_trains;
1836 break;
1837 case VEH_ROAD:
1838 if (!HasAnyRoadTypesAvail(_local_company, (RoadTramType)subtype)) return false;
1839 max = _settings_game.vehicle.max_roadveh;
1840 break;
1841 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1842 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1843 default: NOT_REACHED();
1844 }
1845
1846 /* We can build vehicle infrastructure when we may build the vehicle type */
1847 if (max > 0) {
1848 /* Can we actually build the vehicle type? */
1849 for (const Engine *e : Engine::IterateType(type)) {
1850 if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue;
1851 if (HasBit(e->company_avail, _local_company)) return true;
1852 }
1853 return false;
1854 }
1855
1856 /* We should be able to build infrastructure when we have the actual vehicle type */
1857 for (const Vehicle *v : Vehicle::Iterate()) {
1858 if (v->type == VEH_ROAD && GetRoadTramType(RoadVehicle::From(v)->roadtype) != (RoadTramType)subtype) continue;
1859 if (v->owner == _local_company && v->type == type) return true;
1860 }
1861
1862 return false;
1863 }
1864
1865
1866 /**
1867 * Determines the #LiveryScheme for a vehicle.
1868 * @param engine_type Engine of the vehicle.
1869 * @param parent_engine_type Engine of the front vehicle, #INVALID_ENGINE if vehicle is at front itself.
1870 * @param v the vehicle, \c nullptr if in purchase list etc.
1871 * @return livery scheme to use.
1872 */
GetEngineLiveryScheme(EngineID engine_type,EngineID parent_engine_type,const Vehicle * v)1873 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1874 {
1875 CargoID cargo_type = v == nullptr ? (CargoID)CT_INVALID : v->cargo_type;
1876 const Engine *e = Engine::Get(engine_type);
1877 switch (e->type) {
1878 default: NOT_REACHED();
1879 case VEH_TRAIN:
1880 if (v != nullptr && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1881 /* Wagonoverrides use the colour scheme of the front engine.
1882 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1883 engine_type = parent_engine_type;
1884 e = Engine::Get(engine_type);
1885 /* Note: Luckily cargo_type is not needed for engines */
1886 }
1887
1888 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1889 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1890 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1891 if (!CargoSpec::Get(cargo_type)->is_freight) {
1892 if (parent_engine_type == INVALID_ENGINE) {
1893 return LS_PASSENGER_WAGON_STEAM;
1894 } else {
1895 bool is_mu = HasBit(EngInfo(parent_engine_type)->misc_flags, EF_RAIL_IS_MU);
1896 switch (RailVehInfo(parent_engine_type)->engclass) {
1897 default: NOT_REACHED();
1898 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1899 case EC_DIESEL: return is_mu ? LS_DMU : LS_PASSENGER_WAGON_DIESEL;
1900 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_PASSENGER_WAGON_ELECTRIC;
1901 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1902 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1903 }
1904 }
1905 } else {
1906 return LS_FREIGHT_WAGON;
1907 }
1908 } else {
1909 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1910
1911 switch (e->u.rail.engclass) {
1912 default: NOT_REACHED();
1913 case EC_STEAM: return LS_STEAM;
1914 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1915 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1916 case EC_MONORAIL: return LS_MONORAIL;
1917 case EC_MAGLEV: return LS_MAGLEV;
1918 }
1919 }
1920
1921 case VEH_ROAD:
1922 /* Always use the livery of the front */
1923 if (v != nullptr && parent_engine_type != INVALID_ENGINE) {
1924 engine_type = parent_engine_type;
1925 e = Engine::Get(engine_type);
1926 cargo_type = v->First()->cargo_type;
1927 }
1928 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1929 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1930
1931 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1932 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1933 /* Tram */
1934 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1935 } else {
1936 /* Bus or truck */
1937 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1938 }
1939
1940 case VEH_SHIP:
1941 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1942 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1943 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1944
1945 case VEH_AIRCRAFT:
1946 switch (e->u.air.subtype) {
1947 case AIR_HELI: return LS_HELICOPTER;
1948 case AIR_CTOL: return LS_SMALL_PLANE;
1949 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1950 default: NOT_REACHED();
1951 }
1952 }
1953 }
1954
1955 /**
1956 * Determines the livery for a vehicle.
1957 * @param engine_type EngineID of the vehicle
1958 * @param company Owner of the vehicle
1959 * @param parent_engine_type EngineID of the front vehicle. INVALID_VEHICLE if vehicle is at front itself.
1960 * @param v the vehicle. nullptr if in purchase list etc.
1961 * @param livery_setting The livery settings to use for acquiring the livery information.
1962 * @return livery to use
1963 */
GetEngineLivery(EngineID engine_type,CompanyID company,EngineID parent_engine_type,const Vehicle * v,byte livery_setting)1964 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1965 {
1966 const Company *c = Company::Get(company);
1967 LiveryScheme scheme = LS_DEFAULT;
1968
1969 if (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company)) {
1970 if (v != nullptr) {
1971 const Group *g = Group::GetIfValid(v->First()->group_id);
1972 if (g != nullptr) {
1973 /* Traverse parents until we find a livery or reach the top */
1974 while (g->livery.in_use == 0 && g->parent != INVALID_GROUP) {
1975 g = Group::Get(g->parent);
1976 }
1977 if (g->livery.in_use != 0) return &g->livery;
1978 }
1979 }
1980
1981 /* The default livery is always available for use, but its in_use flag determines
1982 * whether any _other_ liveries are in use. */
1983 if (c->livery[LS_DEFAULT].in_use != 0) {
1984 /* Determine the livery scheme to use */
1985 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1986 }
1987 }
1988
1989 return &c->livery[scheme];
1990 }
1991
1992
GetEngineColourMap(EngineID engine_type,CompanyID company,EngineID parent_engine_type,const Vehicle * v)1993 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1994 {
1995 PaletteID map = (v != nullptr) ? v->colourmap : PAL_NONE;
1996
1997 /* Return cached value if any */
1998 if (map != PAL_NONE) return map;
1999
2000 const Engine *e = Engine::Get(engine_type);
2001
2002 /* Check if we should use the colour map callback */
2003 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
2004 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
2005 /* Failure means "use the default two-colour" */
2006 if (callback != CALLBACK_FAILED) {
2007 static_assert(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2008 map = GB(callback, 0, 14);
2009 /* If bit 14 is set, then the company colours are applied to the
2010 * map else it's returned as-is. */
2011 if (!HasBit(callback, 14)) {
2012 /* Update cache */
2013 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2014 return map;
2015 }
2016 }
2017 }
2018
2019 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
2020
2021 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
2022
2023 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2024 if (!Company::IsValidID(company)) return map;
2025
2026 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2027
2028 map += livery->colour1;
2029 if (twocc) map += livery->colour2 * 16;
2030
2031 /* Update cache */
2032 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2033 return map;
2034 }
2035
2036 /**
2037 * Get the colour map for an engine. This used for unbuilt engines in the user interface.
2038 * @param engine_type ID of engine
2039 * @param company ID of company
2040 * @return A ready-to-use palette modifier
2041 */
GetEnginePalette(EngineID engine_type,CompanyID company)2042 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
2043 {
2044 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, nullptr);
2045 }
2046
2047 /**
2048 * Get the colour map for a vehicle.
2049 * @param v Vehicle to get colour map for
2050 * @return A ready-to-use palette modifier
2051 */
GetVehiclePalette(const Vehicle * v)2052 PaletteID GetVehiclePalette(const Vehicle *v)
2053 {
2054 if (v->IsGroundVehicle()) {
2055 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
2056 }
2057
2058 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
2059 }
2060
2061 /**
2062 * Delete all implicit orders which were not reached.
2063 */
DeleteUnreachedImplicitOrders()2064 void Vehicle::DeleteUnreachedImplicitOrders()
2065 {
2066 if (this->IsGroundVehicle()) {
2067 uint16 &gv_flags = this->GetGroundVehicleFlags();
2068 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2069 /* Do not delete orders, only skip them */
2070 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2071 this->cur_implicit_order_index = this->cur_real_order_index;
2072 InvalidateVehicleOrder(this, 0);
2073 return;
2074 }
2075 }
2076
2077 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2078 while (order != nullptr) {
2079 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2080
2081 if (order->IsType(OT_IMPLICIT)) {
2082 DeleteOrder(this, this->cur_implicit_order_index);
2083 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2084 order = this->GetOrder(this->cur_implicit_order_index);
2085 } else {
2086 /* Skip non-implicit orders, e.g. service-orders */
2087 order = order->next;
2088 this->cur_implicit_order_index++;
2089 }
2090
2091 /* Wrap around */
2092 if (order == nullptr) {
2093 order = this->GetOrder(0);
2094 this->cur_implicit_order_index = 0;
2095 }
2096 }
2097 }
2098
2099 /**
2100 * Prepare everything to begin the loading when arriving at a station.
2101 * @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
2102 */
BeginLoading()2103 void Vehicle::BeginLoading()
2104 {
2105 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2106
2107 uint32 travel_time = this->current_order_time;
2108 if (this->current_order.IsType(OT_GOTO_STATION) &&
2109 this->current_order.GetDestination() == this->last_station_visited) {
2110 this->DeleteUnreachedImplicitOrders();
2111
2112 /* Now both order indices point to the destination station, and we can start loading */
2113 this->current_order.MakeLoading(true);
2114 UpdateVehicleTimetable(this, true);
2115
2116 /* Furthermore add the Non Stop flag to mark that this station
2117 * is the actual destination of the vehicle, which is (for example)
2118 * necessary to be known for HandleTrainLoading to determine
2119 * whether the train is lost or not; not marking a train lost
2120 * that arrives at random stations is bad. */
2121 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2122
2123 } else {
2124 /* We weren't scheduled to stop here. Insert an implicit order
2125 * to show that we are stopping here.
2126 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2127 * the 'wrong' terminal when skipping orders etc. */
2128 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2129 if (this->IsGroundVehicle() &&
2130 (in_list == nullptr || !in_list->IsType(OT_IMPLICIT) ||
2131 in_list->GetDestination() != this->last_station_visited)) {
2132 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2133 /* Do not create consecutive duplicates of implicit orders */
2134 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2135 if (prev_order == nullptr ||
2136 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2137 prev_order->GetDestination() != this->last_station_visited) {
2138
2139 /* Prefer deleting implicit orders instead of inserting new ones,
2140 * so test whether the right order follows later. In case of only
2141 * implicit orders treat the last order in the list like an
2142 * explicit one, except if the overall number of orders surpasses
2143 * IMPLICIT_ORDER_ONLY_CAP. */
2144 int target_index = this->cur_implicit_order_index;
2145 bool found = false;
2146 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2147 const Order *order = this->GetOrder(target_index);
2148 if (order == nullptr) break; // No orders.
2149 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2150 found = true;
2151 break;
2152 }
2153 target_index++;
2154 if (target_index >= this->orders.list->GetNumOrders()) {
2155 if (this->GetNumManualOrders() == 0 &&
2156 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2157 break;
2158 }
2159 target_index = 0;
2160 }
2161 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2162 }
2163
2164 if (found) {
2165 if (suppress_implicit_orders) {
2166 /* Skip to the found order */
2167 this->cur_implicit_order_index = target_index;
2168 InvalidateVehicleOrder(this, 0);
2169 } else {
2170 /* Delete all implicit orders up to the station we just reached */
2171 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2172 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2173 if (order->IsType(OT_IMPLICIT)) {
2174 DeleteOrder(this, this->cur_implicit_order_index);
2175 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2176 order = this->GetOrder(this->cur_implicit_order_index);
2177 } else {
2178 /* Skip non-implicit orders, e.g. service-orders */
2179 order = order->next;
2180 this->cur_implicit_order_index++;
2181 }
2182
2183 /* Wrap around */
2184 if (order == nullptr) {
2185 order = this->GetOrder(0);
2186 this->cur_implicit_order_index = 0;
2187 }
2188 assert(order != nullptr);
2189 }
2190 }
2191 } else if (!suppress_implicit_orders &&
2192 ((this->orders.list == nullptr ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2193 Order::CanAllocateItem()) {
2194 /* Insert new implicit order */
2195 Order *implicit_order = new Order();
2196 implicit_order->MakeImplicit(this->last_station_visited);
2197 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2198 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2199
2200 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2201 * Reenable it for this vehicle */
2202 uint16 &gv_flags = this->GetGroundVehicleFlags();
2203 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2204 }
2205 }
2206 }
2207 this->current_order.MakeLoading(false);
2208 }
2209
2210 if (this->last_loading_station != INVALID_STATION &&
2211 this->last_loading_station != this->last_station_visited &&
2212 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2213 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2214 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited, travel_time);
2215 }
2216
2217 PrepareUnload(this);
2218
2219 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2220 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2221 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2222 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2223
2224 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2225 this->cur_speed = 0;
2226 this->MarkDirty();
2227 }
2228
2229 /**
2230 * Return all reserved cargo packets to the station and reset all packets
2231 * staged for transfer.
2232 * @param st the station where the reserved packets should go.
2233 */
CancelReservation(StationID next,Station * st)2234 void Vehicle::CancelReservation(StationID next, Station *st)
2235 {
2236 for (Vehicle *v = this; v != nullptr; v = v->next) {
2237 VehicleCargoList &cargo = v->cargo;
2238 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2239 Debug(misc, 1, "cancelling cargo reservation");
2240 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2241 cargo.SetTransferLoadPlace(st->xy);
2242 }
2243 cargo.KeepAll();
2244 }
2245 }
2246
2247 /**
2248 * Perform all actions when leaving a station.
2249 * @pre this->current_order.IsType(OT_LOADING)
2250 */
LeaveStation()2251 void Vehicle::LeaveStation()
2252 {
2253 assert(this->current_order.IsType(OT_LOADING));
2254
2255 delete this->cargo_payment;
2256 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
2257
2258 /* Only update the timetable if the vehicle was supposed to stop here. */
2259 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2260
2261 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2262 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2263 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2264 /* Refresh next hop stats to make sure we've done that at least once
2265 * during the stop and that refit_cap == cargo_cap for each vehicle in
2266 * the consist. */
2267 this->ResetRefitCaps();
2268 LinkRefresher::Run(this);
2269
2270 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2271 this->last_loading_station = this->last_station_visited;
2272 } else {
2273 /* if the vehicle couldn't load and had to unload or transfer everything
2274 * set the last loading station to invalid as it will leave empty. */
2275 this->last_loading_station = INVALID_STATION;
2276 }
2277 }
2278
2279 this->current_order.MakeLeaveStation();
2280 Station *st = Station::Get(this->last_station_visited);
2281 this->CancelReservation(INVALID_STATION, st);
2282 st->loading_vehicles.remove(this);
2283
2284 HideFillingPercent(&this->fill_percent_te_id);
2285 trip_occupancy = CalcPercentVehicleFilled(this, nullptr);
2286
2287 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2288 /* Trigger station animation (trains only) */
2289 if (IsTileType(this->tile, MP_STATION)) {
2290 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2291 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2292 }
2293
2294 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2295 }
2296
2297 this->MarkDirty();
2298 }
2299
2300 /**
2301 * Reset all refit_cap in the consist to cargo_cap.
2302 */
ResetRefitCaps()2303 void Vehicle::ResetRefitCaps()
2304 {
2305 for (Vehicle *v = this; v != nullptr; v = v->Next()) v->refit_cap = v->cargo_cap;
2306 }
2307
2308 /**
2309 * Handle the loading of the vehicle; when not it skips through dummy
2310 * orders and does nothing in all other cases.
2311 * @param mode is the non-first call for this vehicle in this tick?
2312 */
HandleLoading(bool mode)2313 void Vehicle::HandleLoading(bool mode)
2314 {
2315 switch (this->current_order.GetType()) {
2316 case OT_LOADING: {
2317 uint wait_time = std::max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2318
2319 /* Not the first call for this tick, or still loading */
2320 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2321
2322 this->PlayLeaveStationSound();
2323
2324 this->LeaveStation();
2325
2326 /* Only advance to next order if we just loaded at the current one */
2327 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2328 if (order == nullptr ||
2329 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2330 order->GetDestination() != this->last_station_visited) {
2331 return;
2332 }
2333 break;
2334 }
2335
2336 case OT_DUMMY: break;
2337
2338 default: return;
2339 }
2340
2341 this->IncrementImplicitOrderIndex();
2342 }
2343
2344 /**
2345 * Get a map of cargoes and free capacities in the consist.
2346 * @param capacities Map to be filled with cargoes and capacities.
2347 */
GetConsistFreeCapacities(SmallMap<CargoID,uint> & capacities) const2348 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2349 {
2350 for (const Vehicle *v = this; v != nullptr; v = v->Next()) {
2351 if (v->cargo_cap == 0) continue;
2352 std::pair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2353 if (pair == capacities.End()) {
2354 capacities.push_back({v->cargo_type, v->cargo_cap - v->cargo.StoredCount()});
2355 } else {
2356 pair->second += v->cargo_cap - v->cargo.StoredCount();
2357 }
2358 }
2359 }
2360
GetConsistTotalCapacity() const2361 uint Vehicle::GetConsistTotalCapacity() const
2362 {
2363 uint result = 0;
2364 for (const Vehicle *v = this; v != nullptr; v = v->Next()) {
2365 result += v->cargo_cap;
2366 }
2367 return result;
2368 }
2369
2370 /**
2371 * Send this vehicle to the depot using the given command(s).
2372 * @param flags the command flags (like execute and such).
2373 * @param command the command to execute.
2374 * @return the cost of the depot action.
2375 */
SendToDepot(DoCommandFlag flags,DepotCommand command)2376 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2377 {
2378 CommandCost ret = CheckOwnership(this->owner);
2379 if (ret.Failed()) return ret;
2380
2381 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2382 if (this->IsStoppedInDepot()) return CMD_ERROR;
2383
2384 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2385 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2386 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2387 /* We called with a different DEPOT_SERVICE setting.
2388 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2389 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2390 if (flags & DC_EXEC) {
2391 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2392 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2393 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2394 }
2395 return CommandCost();
2396 }
2397
2398 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancellation of depot orders
2399 if (flags & DC_EXEC) {
2400 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2401 * then skip to the next order; effectively cancelling this forced service */
2402 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2403
2404 if (this->IsGroundVehicle()) {
2405 uint16 &gv_flags = this->GetGroundVehicleFlags();
2406 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2407 }
2408
2409 this->current_order.MakeDummy();
2410 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2411 }
2412 return CommandCost();
2413 }
2414
2415 TileIndex location;
2416 DestinationID destination;
2417 bool reverse;
2418 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};
2419 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2420
2421 if (flags & DC_EXEC) {
2422 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2423
2424 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2425 uint16 &gv_flags = this->GetGroundVehicleFlags();
2426 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2427 }
2428
2429 this->SetDestTile(location);
2430 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2431 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2432 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2433
2434 /* If there is no depot in front and the train is not already reversing, reverse automatically (trains only) */
2435 if (this->type == VEH_TRAIN && (reverse ^ HasBit(Train::From(this)->flags, VRF_REVERSING))) {
2436 DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2437 }
2438
2439 if (this->type == VEH_AIRCRAFT) {
2440 Aircraft *a = Aircraft::From(this);
2441 if (a->state == FLYING && a->targetairport != destination) {
2442 /* The aircraft is now heading for a different hangar than the next in the orders */
2443 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2444 AircraftNextAirportPos_and_Order(a);
2445 }
2446 }
2447 }
2448
2449 return CommandCost();
2450
2451 }
2452
2453 /**
2454 * Update the cached visual effect.
2455 * @param allow_power_change true if the wagon-is-powered-state may change.
2456 */
UpdateVisualEffect(bool allow_power_change)2457 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2458 {
2459 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2460 const Engine *e = this->GetEngine();
2461
2462 /* Evaluate properties */
2463 byte visual_effect;
2464 switch (e->type) {
2465 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2466 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2467 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2468 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2469 }
2470
2471 /* Check powered wagon / visual effect callback */
2472 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2473 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2474
2475 if (callback != CALLBACK_FAILED) {
2476 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2477
2478 callback = GB(callback, 0, 8);
2479 /* Avoid accidentally setting 'visual_effect' to the default value
2480 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2481 if (callback == VE_DEFAULT) {
2482 assert(HasBit(callback, VE_DISABLE_EFFECT));
2483 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2484 }
2485 visual_effect = callback;
2486 }
2487 }
2488
2489 /* Apply default values */
2490 if (visual_effect == VE_DEFAULT ||
2491 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2492 /* Only train engines have default effects.
2493 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2494 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2495 if (visual_effect == VE_DEFAULT) {
2496 visual_effect = 1 << VE_DISABLE_EFFECT;
2497 } else {
2498 SetBit(visual_effect, VE_DISABLE_EFFECT);
2499 }
2500 } else {
2501 if (visual_effect == VE_DEFAULT) {
2502 /* Also set the offset */
2503 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2504 }
2505 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2506 }
2507 }
2508
2509 this->vcache.cached_vis_effect = visual_effect;
2510
2511 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2512 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2513 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2514 }
2515 }
2516
2517 static const int8 _vehicle_smoke_pos[8] = {
2518 1, 1, 1, 0, -1, -1, -1, 0
2519 };
2520
2521 /**
2522 * Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
2523 * @param v Vehicle to create effects for.
2524 */
SpawnAdvancedVisualEffect(const Vehicle * v)2525 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2526 {
2527 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2528 if (callback == CALLBACK_FAILED) return;
2529
2530 uint count = GB(callback, 0, 2);
2531 bool auto_center = HasBit(callback, 13);
2532 bool auto_rotate = !HasBit(callback, 14);
2533
2534 int8 l_center = 0;
2535 if (auto_center) {
2536 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2537 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2538 } else {
2539 /* For trains: Compute offset from vehicle position to sprite position */
2540 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2541 }
2542
2543 Direction l_dir = v->direction;
2544 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2545 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2546
2547 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2548 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2549
2550 for (uint i = 0; i < count; i++) {
2551 uint32 reg = GetRegister(0x100 + i);
2552 uint type = GB(reg, 0, 8);
2553 int8 x = GB(reg, 8, 8);
2554 int8 y = GB(reg, 16, 8);
2555 int8 z = GB(reg, 24, 8);
2556
2557 if (auto_rotate) {
2558 int8 l = x;
2559 int8 t = y;
2560 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2561 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2562 }
2563
2564 if (type >= 0xF0) {
2565 switch (type) {
2566 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2567 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2568 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2569 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2570 default: break;
2571 }
2572 }
2573 }
2574 }
2575
2576 /**
2577 * Draw visual effects (smoke and/or sparks) for a vehicle chain.
2578 * @pre this->IsPrimaryVehicle()
2579 */
ShowVisualEffect() const2580 void Vehicle::ShowVisualEffect() const
2581 {
2582 assert(this->IsPrimaryVehicle());
2583 bool sound = false;
2584
2585 /* Do not show any smoke when:
2586 * - vehicle smoke is disabled by the player
2587 * - the vehicle is slowing down or stopped (by the player)
2588 * - the vehicle is moving very slowly
2589 */
2590 if (_settings_game.vehicle.smoke_amount == 0 ||
2591 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2592 this->cur_speed < 2) {
2593 return;
2594 }
2595
2596 /* Use the speed as limited by underground and orders. */
2597 uint max_speed = this->GetCurrentMaxSpeed();
2598
2599 if (this->type == VEH_TRAIN) {
2600 const Train *t = Train::From(this);
2601 /* For trains, do not show any smoke when:
2602 * - the train is reversing
2603 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2604 */
2605 if (HasBit(t->flags, VRF_REVERSING) ||
2606 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2607 t->cur_speed >= max_speed)) {
2608 return;
2609 }
2610 }
2611
2612 const Vehicle *v = this;
2613
2614 do {
2615 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2616 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2617 VisualEffectSpawnModel effect_model = VESM_NONE;
2618 if (advanced) {
2619 effect_offset = VE_OFFSET_CENTRE;
2620 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2621 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2622 } else {
2623 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2624 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2625 static_assert((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2626 static_assert((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2627 static_assert((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2628 }
2629
2630 /* Show no smoke when:
2631 * - Smoke has been disabled for this vehicle
2632 * - The vehicle is not visible
2633 * - The vehicle is under a bridge
2634 * - The vehicle is on a depot tile
2635 * - The vehicle is on a tunnel tile
2636 * - The vehicle is a train engine that is currently unpowered */
2637 if (effect_model == VESM_NONE ||
2638 v->vehstatus & VS_HIDDEN ||
2639 IsBridgeAbove(v->tile) ||
2640 IsDepotTile(v->tile) ||
2641 IsTunnelTile(v->tile) ||
2642 (v->type == VEH_TRAIN &&
2643 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2644 continue;
2645 }
2646
2647 EffectVehicleType evt = EV_END;
2648 switch (effect_model) {
2649 case VESM_STEAM:
2650 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2651 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2652 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2653 * REGULATION:
2654 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2655 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2656 evt = EV_STEAM_SMOKE;
2657 }
2658 break;
2659
2660 case VESM_DIESEL: {
2661 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2662 * when smoke emission stops.
2663 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2664 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2665 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2666 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2667 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2668 * maximum speed no diesel_smoke is emitted.
2669 * REGULATION:
2670 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2671 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2672 int power_weight_effect = 0;
2673 if (v->type == VEH_TRAIN) {
2674 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2675 }
2676 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2677 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2678 evt = EV_DIESEL_SMOKE;
2679 }
2680 break;
2681 }
2682
2683 case VESM_ELECTRIC:
2684 /* Electric train's spark - more often occurs when train is departing (more load)
2685 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2686 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2687 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2688 * REGULATION:
2689 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2690 if (GB(v->tick_counter, 0, 2) == 0 &&
2691 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2692 evt = EV_ELECTRIC_SPARK;
2693 }
2694 break;
2695
2696 default:
2697 NOT_REACHED();
2698 }
2699
2700 if (evt != EV_END && advanced) {
2701 sound = true;
2702 SpawnAdvancedVisualEffect(v);
2703 } else if (evt != EV_END) {
2704 sound = true;
2705
2706 /* The effect offset is relative to a point 4 units behind the vehicle's
2707 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2708 * correction factor. */
2709 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2710
2711 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2712 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2713
2714 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2715 x = -x;
2716 y = -y;
2717 }
2718
2719 CreateEffectVehicleRel(v, x, y, 10, evt);
2720 }
2721 } while ((v = v->Next()) != nullptr);
2722
2723 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2724 }
2725
2726 /**
2727 * Set the next vehicle of this vehicle.
2728 * @param next the next vehicle. nullptr removes the next vehicle.
2729 */
SetNext(Vehicle * next)2730 void Vehicle::SetNext(Vehicle *next)
2731 {
2732 assert(this != next);
2733
2734 if (this->next != nullptr) {
2735 /* We had an old next vehicle. Update the first and previous pointers */
2736 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2737 v->first = this->next;
2738 }
2739 this->next->previous = nullptr;
2740 }
2741
2742 this->next = next;
2743
2744 if (this->next != nullptr) {
2745 /* A new next vehicle. Update the first and previous pointers */
2746 if (this->next->previous != nullptr) this->next->previous->next = nullptr;
2747 this->next->previous = this;
2748 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2749 v->first = this->first;
2750 }
2751 }
2752 }
2753
2754 /**
2755 * Adds this vehicle to a shared vehicle chain.
2756 * @param shared_chain a vehicle of the chain with shared vehicles.
2757 * @pre !this->IsOrderListShared()
2758 */
AddToShared(Vehicle * shared_chain)2759 void Vehicle::AddToShared(Vehicle *shared_chain)
2760 {
2761 assert(this->previous_shared == nullptr && this->next_shared == nullptr);
2762
2763 if (shared_chain->orders.list == nullptr) {
2764 assert(shared_chain->previous_shared == nullptr);
2765 assert(shared_chain->next_shared == nullptr);
2766 this->orders.list = shared_chain->orders.list = new OrderList(nullptr, shared_chain);
2767 }
2768
2769 this->next_shared = shared_chain->next_shared;
2770 this->previous_shared = shared_chain;
2771
2772 shared_chain->next_shared = this;
2773
2774 if (this->next_shared != nullptr) this->next_shared->previous_shared = this;
2775
2776 shared_chain->orders.list->AddVehicle(this);
2777 }
2778
2779 /**
2780 * Removes the vehicle from the shared order list.
2781 */
RemoveFromShared()2782 void Vehicle::RemoveFromShared()
2783 {
2784 /* Remember if we were first and the old window number before RemoveVehicle()
2785 * as this changes first if needed. */
2786 bool were_first = (this->FirstShared() == this);
2787 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2788
2789 this->orders.list->RemoveVehicle(this);
2790
2791 if (!were_first) {
2792 /* We are not the first shared one, so only relink our previous one. */
2793 this->previous_shared->next_shared = this->NextShared();
2794 }
2795
2796 if (this->next_shared != nullptr) this->next_shared->previous_shared = this->previous_shared;
2797
2798
2799 if (this->orders.list->GetNumVehicles() == 1) {
2800 /* When there is only one vehicle, remove the shared order list window. */
2801 CloseWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2802 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
2803 } else if (were_first) {
2804 /* If we were the first one, update to the new first one.
2805 * Note: FirstShared() is already the new first */
2806 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2807 }
2808
2809 this->next_shared = nullptr;
2810 this->previous_shared = nullptr;
2811 }
2812
VehiclesYearlyLoop()2813 void VehiclesYearlyLoop()
2814 {
2815 for (Vehicle *v : Vehicle::Iterate()) {
2816 if (v->IsPrimaryVehicle()) {
2817 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2818 Money profit = v->GetDisplayProfitThisYear();
2819 if (v->age >= 730 && profit < 0) {
2820 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2821 SetDParam(0, v->index);
2822 SetDParam(1, profit);
2823 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2824 }
2825 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2826 }
2827
2828 v->profit_last_year = v->profit_this_year;
2829 v->profit_this_year = 0;
2830 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2831 }
2832 }
2833 GroupStatistics::UpdateProfits();
2834 SetWindowClassesDirty(WC_TRAINS_LIST);
2835 SetWindowClassesDirty(WC_SHIPS_LIST);
2836 SetWindowClassesDirty(WC_ROADVEH_LIST);
2837 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2838 }
2839
2840
2841 /**
2842 * Can this station be used by the given engine type?
2843 * @param engine_type the type of vehicles to test
2844 * @param st the station to test for
2845 * @return true if and only if the vehicle of the type can use this station.
2846 * @note For road vehicles the Vehicle is needed to determine whether it can
2847 * use the station. This function will return true for road vehicles
2848 * when at least one of the facilities is available.
2849 */
CanVehicleUseStation(EngineID engine_type,const Station * st)2850 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2851 {
2852 const Engine *e = Engine::GetIfValid(engine_type);
2853 assert(e != nullptr);
2854
2855 switch (e->type) {
2856 case VEH_TRAIN:
2857 return (st->facilities & FACIL_TRAIN) != 0;
2858
2859 case VEH_ROAD:
2860 /* For road vehicles we need the vehicle to know whether it can actually
2861 * use the station, but if it doesn't have facilities for RVs it is
2862 * certainly not possible that the station can be used. */
2863 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2864
2865 case VEH_SHIP:
2866 return (st->facilities & FACIL_DOCK) != 0;
2867
2868 case VEH_AIRCRAFT:
2869 return (st->facilities & FACIL_AIRPORT) != 0 &&
2870 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2871
2872 default:
2873 return false;
2874 }
2875 }
2876
2877 /**
2878 * Can this station be used by the given vehicle?
2879 * @param v the vehicle to test
2880 * @param st the station to test for
2881 * @return true if and only if the vehicle can use this station.
2882 */
CanVehicleUseStation(const Vehicle * v,const Station * st)2883 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2884 {
2885 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != nullptr;
2886
2887 return CanVehicleUseStation(v->engine_type, st);
2888 }
2889
2890 /**
2891 * Access the ground vehicle cache of the vehicle.
2892 * @pre The vehicle is a #GroundVehicle.
2893 * @return #GroundVehicleCache of the vehicle.
2894 */
GetGroundVehicleCache()2895 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2896 {
2897 assert(this->IsGroundVehicle());
2898 if (this->type == VEH_TRAIN) {
2899 return &Train::From(this)->gcache;
2900 } else {
2901 return &RoadVehicle::From(this)->gcache;
2902 }
2903 }
2904
2905 /**
2906 * Access the ground vehicle cache of the vehicle.
2907 * @pre The vehicle is a #GroundVehicle.
2908 * @return #GroundVehicleCache of the vehicle.
2909 */
GetGroundVehicleCache() const2910 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2911 {
2912 assert(this->IsGroundVehicle());
2913 if (this->type == VEH_TRAIN) {
2914 return &Train::From(this)->gcache;
2915 } else {
2916 return &RoadVehicle::From(this)->gcache;
2917 }
2918 }
2919
2920 /**
2921 * Access the ground vehicle flags of the vehicle.
2922 * @pre The vehicle is a #GroundVehicle.
2923 * @return #GroundVehicleFlags of the vehicle.
2924 */
GetGroundVehicleFlags()2925 uint16 &Vehicle::GetGroundVehicleFlags()
2926 {
2927 assert(this->IsGroundVehicle());
2928 if (this->type == VEH_TRAIN) {
2929 return Train::From(this)->gv_flags;
2930 } else {
2931 return RoadVehicle::From(this)->gv_flags;
2932 }
2933 }
2934
2935 /**
2936 * Access the ground vehicle flags of the vehicle.
2937 * @pre The vehicle is a #GroundVehicle.
2938 * @return #GroundVehicleFlags of the vehicle.
2939 */
GetGroundVehicleFlags() const2940 const uint16 &Vehicle::GetGroundVehicleFlags() const
2941 {
2942 assert(this->IsGroundVehicle());
2943 if (this->type == VEH_TRAIN) {
2944 return Train::From(this)->gv_flags;
2945 } else {
2946 return RoadVehicle::From(this)->gv_flags;
2947 }
2948 }
2949
2950 /**
2951 * Calculates the set of vehicles that will be affected by a given selection.
2952 * @param[in,out] set Set of affected vehicles.
2953 * @param v First vehicle of the selection.
2954 * @param num_vehicles Number of vehicles in the selection (not counting articulated parts).
2955 * @pre \a set must be empty.
2956 * @post \a set will contain the vehicles that will be refitted.
2957 */
GetVehicleSet(VehicleSet & set,Vehicle * v,uint8 num_vehicles)2958 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2959 {
2960 if (v->type == VEH_TRAIN) {
2961 Train *u = Train::From(v);
2962 /* Only include whole vehicles, so start with the first articulated part */
2963 u = u->GetFirstEnginePart();
2964
2965 /* Include num_vehicles vehicles, not counting articulated parts */
2966 for (; u != nullptr && num_vehicles > 0; num_vehicles--) {
2967 do {
2968 /* Include current vehicle in the selection. */
2969 include(set, u->index);
2970
2971 /* If the vehicle is multiheaded, add the other part too. */
2972 if (u->IsMultiheaded()) include(set, u->other_multiheaded_part->index);
2973
2974 u = u->Next();
2975 } while (u != nullptr && u->IsArticulatedPart());
2976 }
2977 }
2978 }
2979