OpenTTD: src/vehicle.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

vehicle.cpp
Go to the documentation of this file.
1 /* $Id: vehicle.cpp 27986 2018-03-11 13:23:26Z frosch $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
9
12 #include "stdafx.h"
13 #include "error.h"
14 #include "roadveh.h"
15 #include "ship.h"
16 #include "spritecache.h"
17 #include "timetable.h"
18 #include "viewport_func.h"
19 #include "news_func.h"
20 #include "command_func.h"
21 #include "company_func.h"
22 #include "train.h"
23 #include "aircraft.h"
24 #include "newgrf_debug.h"
25 #include "newgrf_sound.h"
26 #include "newgrf_station.h"
27 #include "group_gui.h"
28 #include "strings_func.h"
29 #include "zoom_func.h"
30 #include "date_func.h"
31 #include "vehicle_func.h"
32 #include "autoreplace_func.h"
33 #include "autoreplace_gui.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "depot_func.h"
37 #include "network/network.h"
38 #include "core/pool_func.hpp"
39 #include "economy_base.h"
40 #include "articulated_vehicles.h"
41 #include "roadstop_base.h"
42 #include "core/random_func.hpp"
43 #include "core/backup_type.hpp"
44 #include "order_backup.h"
45 #include "sound_func.h"
46 #include "effectvehicle_func.h"
47 #include "effectvehicle_base.h"
48 #include "vehiclelist.h"
49 #include "bridge_map.h"
50 #include "tunnel_map.h"
51 #include "depot_map.h"
52 #include "gamelog.h"
53 #include "linkgraph/linkgraph.h"
54 #include "linkgraph/refresh.h"
55
56 #include "table/strings.h"
57
58 #include "safeguards.h"
59
60 /* Number of bits in the hash to use from each vehicle coord */
61 static const uint GEN_HASHX_BITS = 6;
62 static const uint GEN_HASHY_BITS = 6;
63
64 /* Size of each hash bucket */
65 static const uint GEN_HASHX_BUCKET_BITS = 7;
66 static const uint GEN_HASHY_BUCKET_BITS = 6;
67
68 /* Compute hash for vehicle coord */
69 #define GEN_HASHX(x) GB((x), GEN_HASHX_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHX_BITS)
70 #define GEN_HASHY(y) (GB((y), GEN_HASHY_BUCKET_BITS + ZOOM_LVL_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS)
71 #define GEN_HASH(x, y) (GEN_HASHY(y) + GEN_HASHX(x))
72
73 /* Maximum size until hash repeats */
74 static const int GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_LVL_SHIFT);
75 static const int GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_LVL_SHIFT);
76
77 /* Increments to reach next bucket in hash table */
78 static const int GEN_HASHX_INC = 1;
79 static const int GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
80
81 /* Mask to wrap-around buckets */
82 static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
83 static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
84
85 VehicleID _new_vehicle_id;
86 uint16 _returned_refit_capacity;
87 uint16 _returned_mail_refit_capacity;
88
89
91 VehiclePool _vehicle_pool("Vehicle");
92 INSTANTIATE_POOL_METHODS(Vehicle)
93
94
95
99 void VehicleSpriteSeq::GetBounds(Rect *bounds) const
100 {
101 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
102 for (uint i = 0; i < this->count; ++i) {
103 const Sprite *spr = GetSprite(this->seq[i].sprite, ST_NORMAL);
104 if (i == 0) {
105 bounds->left = spr->x_offs;
106 bounds->top = spr->y_offs;
107 bounds->right = spr->width + spr->x_offs - 1;
108 bounds->bottom = spr->height + spr->y_offs - 1;
109 } else {
110 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
111 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
112 int right = spr->width + spr->x_offs - 1;
113 int bottom = spr->height + spr->y_offs - 1;
114 if (right > bounds->right) bounds->right = right;
115 if (bottom > bounds->bottom) bounds->bottom = bottom;
116 }
117 }
118 }
119
127 void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
128 {
129 for (uint i = 0; i < this->count; ++i) {
130 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
131 DrawSprite(this->seq[i].sprite, pal, x, y);
132 }
133 }
134
141 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
142 {
143 /* We can always generate the Company pointer when we have the vehicle.
144 * However this takes time and since the Company pointer is often present
145 * when this function is called then it's faster to pass the pointer as an
146 * argument rather than finding it again. */
147 assert(c == Company::Get(this->owner));
148
149 if (use_renew_setting && !c->settings.engine_renew) return false;
150 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
151
152 /* Only engines need renewing */
153 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
154
155 return true;
156 }
157
163 void VehicleServiceInDepot(Vehicle *v)
164 {
165 assert(v != NULL);
166 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
167
168 do {
169 v->date_of_last_service = _date;
170 v->breakdowns_since_last_service = 0;
171 v->reliability = v->GetEngine()->reliability;
172 /* Prevent vehicles from breaking down directly after exiting the depot. */
173 v->breakdown_chance /= 4;
174 v = v->Next();
175 } while (v != NULL && v->HasEngineType());
176 }
177
184 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 != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
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 uint32 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
252 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
260 uint Vehicle::Crash(bool flooded)
261 {
262 assert((this->vehstatus & VS_CRASHED) == 0);
263 assert(this->Previous() == NULL); // 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 != NULL; 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 == NULL); // cleared by ~CargoPayment
284
285 return RandomRange(pass + 1); // Randomise deceased passengers.
286 }
287
288
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 == NULL) 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, "%s", buffer + 3);
319
320 SetDParam(1, engine);
321 GetString(buffer, part2, lastof(buffer));
322 DEBUG(grf, 0, "%s", buffer + 3);
323 }
324
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
345 Vehicle::Vehicle(VehicleType type)
346 {
347 this->type = type;
348 this->coord.left = INVALID_COORD;
349 this->group_id = DEFAULT_GROUP;
350 this->fill_percent_te_id = INVALID_TE_ID;
351 this->first = this;
352 this->colourmap = PAL_NONE;
353 this->cargo_age_counter = 1;
354 this->last_station_visited = INVALID_STATION;
355 this->last_loading_station = INVALID_STATION;
356 }
357
362 byte VehicleRandomBits()
363 {
364 return GB(Random(), 0, 8);
365 }
366
367 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
368 * lookup times at the expense of memory usage. */
369 const int HASH_BITS = 7;
370 const int HASH_SIZE = 1 << HASH_BITS;
371 const int HASH_MASK = HASH_SIZE - 1;
372 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
373 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
374
375 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
376 * Profiling results show that 0 is fastest. */
377 const int HASH_RES = 0;
378
379 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
380
381 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
382 {
383 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
384 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
385 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
386 for (; v != NULL; v = v->hash_tile_next) {
387 Vehicle *a = proc(v, data);
388 if (find_first && a != NULL) return a;
389 }
390 if (x == xu) break;
391 }
392 if (y == yu) break;
393 }
394
395 return NULL;
396 }
397
398
410 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
411 {
412 const int COLL_DIST = 6;
413
414 /* Hash area to scan is from xl,yl to xu,yu */
415 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
416 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
417 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
418 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
419
420 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
421 }
422
437 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
438 {
439 VehicleFromPosXY(x, y, data, proc, false);
440 }
441
453 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
454 {
455 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
456 }
457
468 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
469 {
470 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
471 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
472
473 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
474 for (; v != NULL; v = v->hash_tile_next) {
475 if (v->tile != tile) continue;
476
477 Vehicle *a = proc(v, data);
478 if (find_first && a != NULL) return a;
479 }
480
481 return NULL;
482 }
483
497 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
498 {
499 VehicleFromPos(tile, data, proc, false);
500 }
501
512 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
513 {
514 return VehicleFromPos(tile, data, proc, true) != NULL;
515 }
516
523 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
524 {
525 int z = *(int*)data;
526
527 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
528 if (v->z_pos > z) return NULL;
529
530 return v;
531 }
532
538 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
539 {
540 int z = GetTileMaxPixelZ(tile);
541
542 /* Value v is not safe in MP games, however, it is used to generate a local
543 * error message only (which may be different for different machines).
544 * Such a message does not affect MP synchronisation.
545 */
546 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
547 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
548 return CommandCost();
549 }
550
552 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
553 {
554 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
555 if (v == (const Vehicle *)data) return NULL;
556
557 return v;
558 }
559
567 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
568 {
569 /* Value v is not safe in MP games, however, it is used to generate a local
570 * error message only (which may be different for different machines).
571 * Such a message does not affect MP synchronisation.
572 */
573 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
574 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
575
576 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
577 return CommandCost();
578 }
579
580 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
581 {
582 TrackBits rail_bits = *(TrackBits *)data;
583
584 if (v->type != VEH_TRAIN) return NULL;
585
586 Train *t = Train::From(v);
587 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
588
589 return v;
590 }
591
600 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
601 {
602 /* Value v is not safe in MP games, however, it is used to generate a local
603 * error message only (which may be different for different machines).
604 * Such a message does not affect MP synchronisation.
605 */
606 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
607 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
608 return CommandCost();
609 }
610
611 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
612 {
613 Vehicle **old_hash = v->hash_tile_current;
614 Vehicle **new_hash;
615
616 if (remove) {
617 new_hash = NULL;
618 } else {
619 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
620 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
621 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
622 }
623
624 if (old_hash == new_hash) return;
625
626 /* Remove from the old position in the hash table */
627 if (old_hash != NULL) {
628 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
629 *v->hash_tile_prev = v->hash_tile_next;
630 }
631
632 /* Insert vehicle at beginning of the new position in the hash table */
633 if (new_hash != NULL) {
634 v->hash_tile_next = *new_hash;
635 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
636 v->hash_tile_prev = new_hash;
637 *new_hash = v;
638 }
639
640 /* Remember current hash position */
641 v->hash_tile_current = new_hash;
642 }
643
644 static Vehicle *_vehicle_viewport_hash[1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)];
645
646 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
647 {
648 Vehicle **old_hash, **new_hash;
649 int old_x = v->coord.left;
650 int old_y = v->coord.top;
651
652 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
653 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
654
655 if (old_hash == new_hash) return;
656
657 /* remove from hash table? */
658 if (old_hash != NULL) {
659 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
660 *v->hash_viewport_prev = v->hash_viewport_next;
661 }
662
663 /* insert into hash table? */
664 if (new_hash != NULL) {
665 v->hash_viewport_next = *new_hash;
666 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
667 v->hash_viewport_prev = new_hash;
668 *new_hash = v;
669 }
670 }
671
672 void ResetVehicleHash()
673 {
674 Vehicle *v;
675 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
676 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
677 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
678 }
679
680 void ResetVehicleColourMap()
681 {
682 Vehicle *v;
683 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
684 }
685
690 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
691 static AutoreplaceMap _vehicles_to_autoreplace;
692
693 void InitializeVehicles()
694 {
695 _vehicles_to_autoreplace.Reset();
696 ResetVehicleHash();
697 }
698
699 uint CountVehiclesInChain(const Vehicle *v)
700 {
701 uint count = 0;
702 do count++; while ((v = v->Next()) != NULL);
703 return count;
704 }
705
710 bool Vehicle::IsEngineCountable() const
711 {
712 switch (this->type) {
713 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
714 case VEH_TRAIN:
715 return !this->IsArticulatedPart() && // tenders and other articulated parts
716 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
717 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
718 case VEH_SHIP: return true;
719 default: return false; // Only count company buildable vehicles
720 }
721 }
722
727 bool Vehicle::HasEngineType() const
728 {
729 switch (this->type) {
730 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
731 case VEH_TRAIN:
732 case VEH_ROAD:
733 case VEH_SHIP: return true;
734 default: return false;
735 }
736 }
737
743 const Engine *Vehicle::GetEngine() const
744 {
745 return Engine::Get(this->engine_type);
746 }
747
753 const GRFFile *Vehicle::GetGRF() const
754 {
755 return this->GetEngine()->GetGRF();
756 }
757
763 uint32 Vehicle::GetGRFID() const
764 {
765 return this->GetEngine()->GetGRFID();
766 }
767
775 void Vehicle::HandlePathfindingResult(bool path_found)
776 {
777 if (path_found) {
778 /* Route found, is the vehicle marked with "lost" flag? */
779 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
780
781 /* Clear the flag as the PF's problem was solved. */
782 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
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 /* Notify user about the event. */
794 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
795 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
796 SetDParam(0, this->index);
797 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
798 }
799 }
800
802 void Vehicle::PreDestructor()
803 {
804 if (CleaningPool()) return;
805
806 if (Station::IsValidID(this->last_station_visited)) {
807 Station *st = Station::Get(this->last_station_visited);
808 st->loading_vehicles.remove(this);
809
810 HideFillingPercent(&this->fill_percent_te_id);
811 this->CancelReservation(INVALID_STATION, st);
812 delete this->cargo_payment;
813 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
814 }
815
816 if (this->IsEngineCountable()) {
817 GroupStatistics::CountEngine(this, -1);
818 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
819 GroupStatistics::UpdateAutoreplace(this->owner);
820
821 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
822 DeleteGroupHighlightOfVehicle(this);
823 }
824
825 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
826 Aircraft *a = Aircraft::From(this);
827 Station *st = GetTargetAirportIfValid(a);
828 if (st != NULL) {
829 const AirportFTA *layout = st->airport.GetFTA()->layout;
830 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
831 }
832 }
833
834
835 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
836 RoadVehicle *v = RoadVehicle::From(this);
837 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
838 /* Leave the drive through roadstop, when you have not already left it. */
839 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
840 }
841 }
842
843 if (this->Previous() == NULL) {
844 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
845 }
846
847 if (this->IsPrimaryVehicle()) {
848 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
849 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
850 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
851 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
852 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
853 SetWindowDirty(WC_COMPANY, this->owner);
854 OrderBackup::ClearVehicle(this);
855 }
856 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
857
858 this->cargo.Truncate();
859 DeleteVehicleOrders(this);
860 DeleteDepotHighlightOfVehicle(this);
861
862 extern void StopGlobalFollowVehicle(const Vehicle *v);
863 StopGlobalFollowVehicle(this);
864
865 ReleaseDisastersTargetingVehicle(this->index);
866 }
867
868 Vehicle::~Vehicle()
869 {
870 if (CleaningPool()) {
871 this->cargo.OnCleanPool();
872 return;
873 }
874
875 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
876 * it may happen that vehicle chain is deleted when visible */
877 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
878
879 Vehicle *v = this->Next();
880 this->SetNext(NULL);
881
882 delete v;
883
884 UpdateVehicleTileHash(this, true);
885 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
886 DeleteVehicleNews(this->index, INVALID_STRING_ID);
887 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
888 }
889
894 void VehicleEnteredDepotThisTick(Vehicle *v)
895 {
896 /* Vehicle should stop in the depot if it was in 'stopping' state */
897 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
898
899 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
900 * stopping in the depot, so we stop it to ensure that it will not reserve
901 * the path out of the depot before we might autoreplace it to a different
902 * engine. The new engine would not own the reserved path we store that we
903 * stopped the vehicle, so autoreplace can start it again */
904 v->vehstatus |= VS_STOPPED;
905 }
906
912 static void RunVehicleDayProc()
913 {
914 if (_game_mode != GM_NORMAL) return;
915
916 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
917 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
918 Vehicle *v = Vehicle::Get(i);
919 if (v == NULL) continue;
920
921 /* Call the 32-day callback if needed */
922 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
923 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
924 if (callback != CALLBACK_FAILED) {
925 if (HasBit(callback, 0)) {
926 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
927 }
928
929 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
930 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
931 if (callback != 0) v->First()->MarkDirty();
932
933 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
934 }
935 }
936
937 /* This is called once per day for each vehicle, but not in the first tick of the day */
938 v->OnNewDay();
939 }
940 }
941
942 void CallVehicleTicks()
943 {
944 _vehicles_to_autoreplace.Clear();
945
946 RunVehicleDayProc();
947
948 Station *st;
949 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
950
951 Vehicle *v;
952 FOR_ALL_VEHICLES(v) {
953 /* Vehicle could be deleted in this tick */
954 if (!v->Tick()) {
955 assert(Vehicle::Get(vehicle_index) == NULL);
956 continue;
957 }
958
959 assert(Vehicle::Get(vehicle_index) == v);
960
961 switch (v->type) {
962 default: break;
963
964 case VEH_TRAIN:
965 case VEH_ROAD:
966 case VEH_AIRCRAFT:
967 case VEH_SHIP: {
968 Vehicle *front = v->First();
969
970 if (v->vcache.cached_cargo_age_period != 0) {
971 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
972 if (--v->cargo_age_counter == 0) {
973 v->cargo.AgeCargo();
974 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
975 }
976 }
977
978 /* Do not play any sound when crashed */
979 if (front->vehstatus & VS_CRASHED) continue;
980
981 /* Do not play any sound when in depot or tunnel */
982 if (v->vehstatus & VS_HIDDEN) continue;
983
984 /* Do not play any sound when stopped */
985 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
986
987 /* Check vehicle type specifics */
988 switch (v->type) {
989 case VEH_TRAIN:
990 if (Train::From(v)->IsWagon()) continue;
991 break;
992
993 case VEH_ROAD:
994 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
995 break;
996
997 case VEH_AIRCRAFT:
998 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
999 break;
1000
1001 default:
1002 break;
1003 }
1004
1005 v->motion_counter += front->cur_speed;
1006 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1007 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1008
1009 /* Play an alternating running sound every 16 ticks */
1010 if (GB(v->tick_counter, 0, 4) == 0) {
1011 /* Play running sound when speed > 0 and not braking */
1012 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
1013 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
1014 }
1015
1016 break;
1017 }
1018 }
1019 }
1020
1021 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
1022 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
1023 v = it->first;
1024 /* Autoreplace needs the current company set as the vehicle owner */
1025 cur_company.Change(v->owner);
1026
1027 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1028 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1029 * they are already leaving the depot again before being replaced. */
1030 if (it->second) v->vehstatus &= ~VS_STOPPED;
1031
1032 /* Store the position of the effect as the vehicle pointer will become invalid later */
1033 int x = v->x_pos;
1034 int y = v->y_pos;
1035 int z = v->z_pos;
1036
1037 const Company *c = Company::Get(_current_company);
1038 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
1039 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
1040 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
1041
1042 if (!IsLocalCompany()) continue;
1043
1044 if (res.Succeeded()) {
1045 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1046 continue;
1047 }
1048
1049 StringID error_message = res.GetErrorMessage();
1050 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1051
1052 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1053
1054 StringID message;
1055 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1056 message = error_message;
1057 } else {
1058 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
1059 }
1060
1061 SetDParam(0, v->index);
1062 SetDParam(1, error_message);
1063 AddVehicleAdviceNewsItem(message, v->index);
1064 }
1065
1066 cur_company.Restore();
1067 }
1068
1073 static void DoDrawVehicle(const Vehicle *v)
1074 {
1075 PaletteID pal = PAL_NONE;
1076
1077 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1078
1079 /* Check whether the vehicle shall be transparent due to the game state */
1080 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1081
1082 if (v->type == VEH_EFFECT) {
1083 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1084 * However, transparent smoke and bubbles look weird, so always hide them. */
1085 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1086 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1087 }
1088
1089 StartSpriteCombine();
1090 for (uint i = 0; i < v->sprite_seq.count; ++i) {
1091 PaletteID pal2 = v->sprite_seq.seq[i].pal;
1092 if (!pal2 || (v->vehstatus & VS_CRASHED)) pal2 = pal;
1093 AddSortableSpriteToDraw(v->sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1094 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1095 }
1096 EndSpriteCombine();
1097 }
1098
1103 void ViewportAddVehicles(DrawPixelInfo *dpi)
1104 {
1105 /* The bounding rectangle */
1106 const int l = dpi->left;
1107 const int r = dpi->left + dpi->width;
1108 const int t = dpi->top;
1109 const int b = dpi->top + dpi->height;
1110
1111 /* The hash area to scan */
1112 int xl, xu, yl, yu;
1113
1114 if (dpi->width + (MAX_VEHICLE_PIXEL_X * ZOOM_LVL_BASE) < GEN_HASHX_SIZE) {
1115 xl = GEN_HASHX(l - MAX_VEHICLE_PIXEL_X * ZOOM_LVL_BASE);
1116 xu = GEN_HASHX(r);
1117 } else {
1118 /* scan whole hash row */
1119 xl = 0;
1120 xu = GEN_HASHX_MASK;
1121 }
1122
1123 if (dpi->height + (MAX_VEHICLE_PIXEL_Y * ZOOM_LVL_BASE) < GEN_HASHY_SIZE) {
1124 yl = GEN_HASHY(t - MAX_VEHICLE_PIXEL_Y * ZOOM_LVL_BASE);
1125 yu = GEN_HASHY(b);
1126 } else {
1127 /* scan whole column */
1128 yl = 0;
1129 yu = GEN_HASHY_MASK;
1130 }
1131
1132 for (int y = yl;; y = (y + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1133 for (int x = xl;; x = (x + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1134 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1135
1136 while (v != NULL) {
1137 if (!(v->vehstatus & VS_HIDDEN) &&
1138 l <= v->coord.right &&
1139 t <= v->coord.bottom &&
1140 r >= v->coord.left &&
1141 b >= v->coord.top) {
1142 DoDrawVehicle(v);
1143 }
1144 v = v->hash_viewport_next;
1145 }
1146
1147 if (x == xu) break;
1148 }
1149
1150 if (y == yu) break;
1151 }
1152 }
1153
1161 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1162 {
1163 Vehicle *found = NULL, *v;
1164 uint dist, best_dist = UINT_MAX;
1165
1166 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1167
1168 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1169 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1170
1171 FOR_ALL_VEHICLES(v) {
1172 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1173 x >= v->coord.left && x <= v->coord.right &&
1174 y >= v->coord.top && y <= v->coord.bottom) {
1175
1176 dist = max(
1177 abs(((v->coord.left + v->coord.right) >> 1) - x),
1178 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1179 );
1180
1181 if (dist < best_dist) {
1182 found = v;
1183 best_dist = dist;
1184 }
1185 }
1186 }
1187
1188 return found;
1189 }
1190
1195 void DecreaseVehicleValue(Vehicle *v)
1196 {
1197 v->value -= v->value >> 8;
1198 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1199 }
1200
1201 static const byte _breakdown_chance[64] = {
1202 3, 3, 3, 3, 3, 3, 3, 3,
1203 4, 4, 5, 5, 6, 6, 7, 7,
1204 8, 8, 9, 9, 10, 10, 11, 11,
1205 12, 13, 13, 13, 13, 14, 15, 16,
1206 17, 19, 21, 25, 28, 31, 34, 37,
1207 40, 44, 48, 52, 56, 60, 64, 68,
1208 72, 80, 90, 100, 110, 120, 130, 140,
1209 150, 170, 190, 210, 230, 250, 250, 250,
1210 };
1211
1212 void CheckVehicleBreakdown(Vehicle *v)
1213 {
1214 int rel, rel_old;
1215
1216 /* decrease reliability */
1217 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1218 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1219
1220 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1221 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1222 v->cur_speed < 5 || _game_mode == GM_MENU) {
1223 return;
1224 }
1225
1226 uint32 r = Random();
1227
1228 /* increase chance of failure */
1229 int chance = v->breakdown_chance + 1;
1230 if (Chance16I(1, 25, r)) chance += 25;
1231 v->breakdown_chance = min(255, chance);
1232
1233 /* calculate reliability value to use in comparison */
1234 rel = v->reliability;
1235 if (v->type == VEH_SHIP) rel += 0x6666;
1236
1237 /* reduced breakdowns? */
1238 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1239
1240 /* check if to break down */
1241 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1242 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1243 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1244 v->breakdown_chance = 0;
1245 }
1246 }
1247
1254 bool Vehicle::HandleBreakdown()
1255 {
1256 /* Possible states for Vehicle::breakdown_ctr
1257 * 0 - vehicle is running normally
1258 * 1 - vehicle is currently broken down
1259 * 2 - vehicle is going to break down now
1260 * >2 - vehicle is counting down to the actual breakdown event */
1261 switch (this->breakdown_ctr) {
1262 case 0:
1263 return false;
1264
1265 case 2:
1266 this->breakdown_ctr = 1;
1267
1268 if (this->breakdowns_since_last_service != 255) {
1269 this->breakdowns_since_last_service++;
1270 }
1271
1272 if (this->type == VEH_AIRCRAFT) {
1273 /* Aircraft just need this flag, the rest is handled elsewhere */
1274 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1275 } else {
1276 this->cur_speed = 0;
1277
1278 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1279 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1280 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1281 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1282 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1283 }
1284
1285 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1286 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1287 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1288 }
1289 }
1290
1291 this->MarkDirty(); // Update graphics after speed is zeroed
1292 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1293 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1294
1295 FALLTHROUGH;
1296 case 1:
1297 /* Aircraft breakdowns end only when arriving at the airport */
1298 if (this->type == VEH_AIRCRAFT) return false;
1299
1300 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1301 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1302 if (--this->breakdown_delay == 0) {
1303 this->breakdown_ctr = 0;
1304 this->MarkDirty();
1305 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1306 }
1307 }
1308 return true;
1309
1310 default:
1311 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1312 return false;
1313 }
1314 }
1315
1320 void AgeVehicle(Vehicle *v)
1321 {
1322 if (v->age < MAX_DAY) {
1323 v->age++;
1324 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1325 }
1326
1327 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1328
1329 int age = v->age - v->max_age;
1330 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1331 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1332 v->reliability_spd_dec <<= 1;
1333 }
1334
1335 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1336
1337 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1338 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1339
1340 /* Don't warn if a renew is active */
1341 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1342
1343 StringID str;
1344 if (age == -DAYS_IN_LEAP_YEAR) {
1345 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1346 } else if (age == 0) {
1347 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1348 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1349 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1350 } else {
1351 return;
1352 }
1353
1354 SetDParam(0, v->index);
1355 AddVehicleAdviceNewsItem(str, v->index);
1356 }
1357
1367 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1368 {
1369 int count = 0;
1370 int max = 0;
1371 int cars = 0;
1372 int unloading = 0;
1373 bool loading = false;
1374
1375 bool is_loading = front->current_order.IsType(OT_LOADING);
1376
1377 /* The station may be NULL when the (colour) string does not need to be set. */
1378 const Station *st = Station::GetIfValid(front->last_station_visited);
1379 assert(colour == NULL || (st != NULL && is_loading));
1380
1381 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1382 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1383
1384 /* Count up max and used */
1385 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1386 count += v->cargo.StoredCount();
1387 max += v->cargo_cap;
1388 if (v->cargo_cap != 0 && colour != NULL) {
1389 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1390 loading |= !order_no_load &&
1391 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1392 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1393 cars++;
1394 }
1395 }
1396
1397 if (colour != NULL) {
1398 if (unloading == 0 && loading) {
1399 *colour = STR_PERCENT_UP;
1400 } else if (unloading == 0 && !loading) {
1401 *colour = STR_PERCENT_NONE;
1402 } else if (cars == unloading || !loading) {
1403 *colour = STR_PERCENT_DOWN;
1404 } else {
1405 *colour = STR_PERCENT_UP_DOWN;
1406 }
1407 }
1408
1409 /* Train without capacity */
1410 if (max == 0) return 100;
1411
1412 /* Return the percentage */
1413 if (count * 2 < max) {
1414 /* Less than 50%; round up, so that 0% means really empty. */
1415 return CeilDiv(count * 100, max);
1416 } else {
1417 /* More than 50%; round down, so that 100% means really full. */
1418 return (count * 100) / max;
1419 }
1420 }
1421
1426 void VehicleEnterDepot(Vehicle *v)
1427 {
1428 /* Always work with the front of the vehicle */
1429 assert(v == v->First());
1430
1431 switch (v->type) {
1432 case VEH_TRAIN: {
1433 Train *t = Train::From(v);
1434 SetWindowClassesDirty(WC_TRAINS_LIST);
1435 /* Clear path reservation */
1436 SetDepotReservation(t->tile, false);
1437 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1438
1439 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1440 t->wait_counter = 0;
1441 t->force_proceed = TFP_NONE;
1442 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1443 t->ConsistChanged(CCF_ARRANGE);
1444 break;
1445 }
1446
1447 case VEH_ROAD:
1448 SetWindowClassesDirty(WC_ROADVEH_LIST);
1449 break;
1450
1451 case VEH_SHIP: {
1452 SetWindowClassesDirty(WC_SHIPS_LIST);
1453 Ship *ship = Ship::From(v);
1454 ship->state = TRACK_BIT_DEPOT;
1455 ship->UpdateCache();
1456 ship->UpdateViewport(true, true);
1457 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1458 break;
1459 }
1460
1461 case VEH_AIRCRAFT:
1462 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1463 HandleAircraftEnterHangar(Aircraft::From(v));
1464 break;
1465 default: NOT_REACHED();
1466 }
1467 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1468
1469 if (v->type != VEH_TRAIN) {
1470 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1471 * 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 */
1472 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1473 }
1474 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1475
1476 v->vehstatus |= VS_HIDDEN;
1477 v->cur_speed = 0;
1478
1479 VehicleServiceInDepot(v);
1480
1481 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1482 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1483 v->MarkDirty();
1484
1485 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1486 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1487
1488 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1489
1490 /* Test whether we are heading for this depot. If not, do nothing.
1491 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1492 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1493 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1494 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1495 /* We are heading for another depot, keep driving. */
1496 return;
1497 }
1498
1499 if (v->current_order.IsRefit()) {
1500 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1501 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1502 cur_company.Restore();
1503
1504 if (cost.Failed()) {
1505 _vehicles_to_autoreplace[v] = false;
1506 if (v->owner == _local_company) {
1507 /* Notify the user that we stopped the vehicle */
1508 SetDParam(0, v->index);
1509 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1510 }
1511 } else if (cost.GetCost() != 0) {
1512 v->profit_this_year -= cost.GetCost() << 8;
1513 if (v->owner == _local_company) {
1514 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1515 }
1516 }
1517 }
1518
1519 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1520 /* Part of orders */
1521 v->DeleteUnreachedImplicitOrders();
1522 UpdateVehicleTimetable(v, true);
1523 v->IncrementImplicitOrderIndex();
1524 }
1525 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1526 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1527 _vehicles_to_autoreplace[v] = false;
1528 /* Invalidate last_loading_station. As the link from the station
1529 * before the stop to the station after the stop can't be predicted
1530 * we shouldn't construct it when the vehicle visits the next stop. */
1531 v->last_loading_station = INVALID_STATION;
1532 if (v->owner == _local_company) {
1533 SetDParam(0, v->index);
1534 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1535 }
1536 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1537 }
1538 v->current_order.MakeDummy();
1539 }
1540 }
1541
1542
1547 void Vehicle::UpdatePosition()
1548 {
1549 UpdateVehicleTileHash(this, false);
1550 }
1551
1557 void Vehicle::UpdateViewport(bool dirty)
1558 {
1559 Rect new_coord;
1560 this->sprite_seq.GetBounds(&new_coord);
1561
1562 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1563 new_coord.left += pt.x;
1564 new_coord.top += pt.y;
1565 new_coord.right += pt.x + 2 * ZOOM_LVL_BASE;
1566 new_coord.bottom += pt.y + 2 * ZOOM_LVL_BASE;
1567
1568 UpdateVehicleViewportHash(this, new_coord.left, new_coord.top);
1569
1570 Rect old_coord = this->coord;
1571 this->coord = new_coord;
1572
1573 if (dirty) {
1574 if (old_coord.left == INVALID_COORD) {
1575 this->MarkAllViewportsDirty();
1576 } else {
1577 ::MarkAllViewportsDirty(
1578 min(old_coord.left, this->coord.left),
1579 min(old_coord.top, this->coord.top),
1580 max(old_coord.right, this->coord.right),
1581 max(old_coord.bottom, this->coord.bottom));
1582 }
1583 }
1584 }
1585
1589 void Vehicle::UpdatePositionAndViewport()
1590 {
1591 this->UpdatePosition();
1592 this->UpdateViewport(true);
1593 }
1594
1598 void Vehicle::MarkAllViewportsDirty() const
1599 {
1600 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1601 }
1602
1608 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1609 {
1610 static const int8 _delta_coord[16] = {
1611 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1612 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1613 };
1614
1615 int x = v->x_pos + _delta_coord[v->direction];
1616 int y = v->y_pos + _delta_coord[v->direction + 8];
1617
1618 GetNewVehiclePosResult gp;
1619 gp.x = x;
1620 gp.y = y;
1621 gp.old_tile = v->tile;
1622 gp.new_tile = TileVirtXY(x, y);
1623 return gp;
1624 }
1625
1626 static const Direction _new_direction_table[] = {
1627 DIR_N, DIR_NW, DIR_W,
1628 DIR_NE, DIR_SE, DIR_SW,
1629 DIR_E, DIR_SE, DIR_S
1630 };
1631
1632 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1633 {
1634 int i = 0;
1635
1636 if (y >= v->y_pos) {
1637 if (y != v->y_pos) i += 3;
1638 i += 3;
1639 }
1640
1641 if (x >= v->x_pos) {
1642 if (x != v->x_pos) i++;
1643 i++;
1644 }
1645
1646 Direction dir = v->direction;
1647
1648 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1649 if (dirdiff == DIRDIFF_SAME) return dir;
1650 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1651 }
1652
1662 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1663 {
1664 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1665 }
1666
1674 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1675 {
1676 /* Find maximum */
1677 const Vehicle *v;
1678 FOR_ALL_VEHICLES(v) {
1679 if (v->type == type && v->owner == owner) {
1680 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1681 }
1682 }
1683
1684 if (this->maxid == 0) return;
1685
1686 /* Reserving 'maxid + 2' because we need:
1687 * - space for the last item (with v->unitnumber == maxid)
1688 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1689 this->cache = CallocT<bool>(this->maxid + 2);
1690
1691 /* Fill the cache */
1692 FOR_ALL_VEHICLES(v) {
1693 if (v->type == type && v->owner == owner) {
1694 this->cache[v->unitnumber] = true;
1695 }
1696 }
1697 }
1698
1700 UnitID FreeUnitIDGenerator::NextID()
1701 {
1702 if (this->maxid <= this->curid) return ++this->curid;
1703
1704 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1705
1706 return this->curid;
1707 }
1708
1714 UnitID GetFreeUnitNumber(VehicleType type)
1715 {
1716 /* Check whether it is allowed to build another vehicle. */
1717 uint max_veh;
1718 switch (type) {
1719 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1720 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1721 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1722 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1723 default: NOT_REACHED();
1724 }
1725
1726 const Company *c = Company::Get(_current_company);
1727 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1728
1729 FreeUnitIDGenerator gen(type, _current_company);
1730
1731 return gen.NextID();
1732 }
1733
1734
1743 bool CanBuildVehicleInfrastructure(VehicleType type)
1744 {
1745 assert(IsCompanyBuildableVehicleType(type));
1746
1747 if (!Company::IsValidID(_local_company)) return false;
1748 if (!_settings_client.gui.disable_unsuitable_building) return true;
1749
1750 UnitID max;
1751 switch (type) {
1752 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1753 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1754 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1755 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1756 default: NOT_REACHED();
1757 }
1758
1759 /* We can build vehicle infrastructure when we may build the vehicle type */
1760 if (max > 0) {
1761 /* Can we actually build the vehicle type? */
1762 const Engine *e;
1763 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1764 if (HasBit(e->company_avail, _local_company)) return true;
1765 }
1766 return false;
1767 }
1768
1769 /* We should be able to build infrastructure when we have the actual vehicle type */
1770 const Vehicle *v;
1771 FOR_ALL_VEHICLES(v) {
1772 if (v->owner == _local_company && v->type == type) return true;
1773 }
1774
1775 return false;
1776 }
1777
1778
1786 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1787 {
1788 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1789 const Engine *e = Engine::Get(engine_type);
1790 switch (e->type) {
1791 default: NOT_REACHED();
1792 case VEH_TRAIN:
1793 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1794 /* Wagonoverrides use the colour scheme of the front engine.
1795 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1796 engine_type = parent_engine_type;
1797 e = Engine::Get(engine_type);
1798 /* Note: Luckily cargo_type is not needed for engines */
1799 }
1800
1801 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1802 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1803 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1804 if (!CargoSpec::Get(cargo_type)->is_freight) {
1805 if (parent_engine_type == INVALID_ENGINE) {
1806 return LS_PASSENGER_WAGON_STEAM;
1807 } else {
1808 switch (RailVehInfo(parent_engine_type)->engclass) {
1809 default: NOT_REACHED();
1810 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1811 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1812 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1813 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1814 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1815 }
1816 }
1817 } else {
1818 return LS_FREIGHT_WAGON;
1819 }
1820 } else {
1821 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1822
1823 switch (e->u.rail.engclass) {
1824 default: NOT_REACHED();
1825 case EC_STEAM: return LS_STEAM;
1826 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1827 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1828 case EC_MONORAIL: return LS_MONORAIL;
1829 case EC_MAGLEV: return LS_MAGLEV;
1830 }
1831 }
1832
1833 case VEH_ROAD:
1834 /* Always use the livery of the front */
1835 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1836 engine_type = parent_engine_type;
1837 e = Engine::Get(engine_type);
1838 cargo_type = v->First()->cargo_type;
1839 }
1840 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1841 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1842
1843 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1844 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1845 /* Tram */
1846 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1847 } else {
1848 /* Bus or truck */
1849 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1850 }
1851
1852 case VEH_SHIP:
1853 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1854 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1855 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1856
1857 case VEH_AIRCRAFT:
1858 switch (e->u.air.subtype) {
1859 case AIR_HELI: return LS_HELICOPTER;
1860 case AIR_CTOL: return LS_SMALL_PLANE;
1861 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1862 default: NOT_REACHED();
1863 }
1864 }
1865 }
1866
1876 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1877 {
1878 const Company *c = Company::Get(company);
1879 LiveryScheme scheme = LS_DEFAULT;
1880
1881 /* The default livery is always available for use, but its in_use flag determines
1882 * whether any _other_ liveries are in use. */
1883 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1884 /* Determine the livery scheme to use */
1885 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1886
1887 /* Switch back to the default scheme if the resolved scheme is not in use */
1888 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1889 }
1890
1891 return &c->livery[scheme];
1892 }
1893
1894
1895 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1896 {
1897 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1898
1899 /* Return cached value if any */
1900 if (map != PAL_NONE) return map;
1901
1902 const Engine *e = Engine::Get(engine_type);
1903
1904 /* Check if we should use the colour map callback */
1905 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1906 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1907 /* Failure means "use the default two-colour" */
1908 if (callback != CALLBACK_FAILED) {
1909 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1910 map = GB(callback, 0, 14);
1911 /* If bit 14 is set, then the company colours are applied to the
1912 * map else it's returned as-is. */
1913 if (!HasBit(callback, 14)) {
1914 /* Update cache */
1915 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1916 return map;
1917 }
1918 }
1919 }
1920
1921 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1922
1923 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1924
1925 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1926 if (!Company::IsValidID(company)) return map;
1927
1928 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1929
1930 map += livery->colour1;
1931 if (twocc) map += livery->colour2 * 16;
1932
1933 /* Update cache */
1934 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1935 return map;
1936 }
1937
1944 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1945 {
1946 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1947 }
1948
1954 PaletteID GetVehiclePalette(const Vehicle *v)
1955 {
1956 if (v->IsGroundVehicle()) {
1957 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1958 }
1959
1960 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1961 }
1962
1966 void Vehicle::DeleteUnreachedImplicitOrders()
1967 {
1968 if (this->IsGroundVehicle()) {
1969 uint16 &gv_flags = this->GetGroundVehicleFlags();
1970 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1971 /* Do not delete orders, only skip them */
1972 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1973 this->cur_implicit_order_index = this->cur_real_order_index;
1974 InvalidateVehicleOrder(this, 0);
1975 return;
1976 }
1977 }
1978
1979 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1980 while (order != NULL) {
1981 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1982
1983 if (order->IsType(OT_IMPLICIT)) {
1984 DeleteOrder(this, this->cur_implicit_order_index);
1985 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1986 order = this->GetOrder(this->cur_implicit_order_index);
1987 } else {
1988 /* Skip non-implicit orders, e.g. service-orders */
1989 order = order->next;
1990 this->cur_implicit_order_index++;
1991 }
1992
1993 /* Wrap around */
1994 if (order == NULL) {
1995 order = this->GetOrder(0);
1996 this->cur_implicit_order_index = 0;
1997 }
1998 }
1999 }
2000
2005 void Vehicle::BeginLoading()
2006 {
2007 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2008
2009 if (this->current_order.IsType(OT_GOTO_STATION) &&
2010 this->current_order.GetDestination() == this->last_station_visited) {
2011 this->DeleteUnreachedImplicitOrders();
2012
2013 /* Now both order indices point to the destination station, and we can start loading */
2014 this->current_order.MakeLoading(true);
2015 UpdateVehicleTimetable(this, true);
2016
2017 /* Furthermore add the Non Stop flag to mark that this station
2018 * is the actual destination of the vehicle, which is (for example)
2019 * necessary to be known for HandleTrainLoading to determine
2020 * whether the train is lost or not; not marking a train lost
2021 * that arrives at random stations is bad. */
2022 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2023
2024 } else {
2025 /* We weren't scheduled to stop here. Insert an implicit order
2026 * to show that we are stopping here.
2027 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2028 * the 'wrong' terminal when skipping orders etc. */
2029 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2030 if (this->IsGroundVehicle() &&
2031 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
2032 in_list->GetDestination() != this->last_station_visited)) {
2033 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2034 /* Do not create consecutive duplicates of implicit orders */
2035 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
2036 if (prev_order == NULL ||
2037 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2038 prev_order->GetDestination() != this->last_station_visited) {
2039
2040 /* Prefer deleting implicit orders instead of inserting new ones,
2041 * so test whether the right order follows later. In case of only
2042 * implicit orders treat the last order in the list like an
2043 * explicit one, except if the overall number of orders surpasses
2044 * IMPLICIT_ORDER_ONLY_CAP. */
2045 int target_index = this->cur_implicit_order_index;
2046 bool found = false;
2047 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2048 const Order *order = this->GetOrder(target_index);
2049 if (order == NULL) break; // No orders.
2050 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2051 found = true;
2052 break;
2053 }
2054 target_index++;
2055 if (target_index >= this->orders.list->GetNumOrders()) {
2056 if (this->GetNumManualOrders() == 0 &&
2057 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2058 break;
2059 }
2060 target_index = 0;
2061 }
2062 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2063 }
2064
2065 if (found) {
2066 if (suppress_implicit_orders) {
2067 /* Skip to the found order */
2068 this->cur_implicit_order_index = target_index;
2069 InvalidateVehicleOrder(this, 0);
2070 } else {
2071 /* Delete all implicit orders up to the station we just reached */
2072 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2073 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2074 if (order->IsType(OT_IMPLICIT)) {
2075 DeleteOrder(this, this->cur_implicit_order_index);
2076 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2077 order = this->GetOrder(this->cur_implicit_order_index);
2078 } else {
2079 /* Skip non-implicit orders, e.g. service-orders */
2080 order = order->next;
2081 this->cur_implicit_order_index++;
2082 }
2083
2084 /* Wrap around */
2085 if (order == NULL) {
2086 order = this->GetOrder(0);
2087 this->cur_implicit_order_index = 0;
2088 }
2089 assert(order != NULL);
2090 }
2091 }
2092 } else if (!suppress_implicit_orders &&
2093 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2094 Order::CanAllocateItem()) {
2095 /* Insert new implicit order */
2096 Order *implicit_order = new Order();
2097 implicit_order->MakeImplicit(this->last_station_visited);
2098 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2099 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2100
2101 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2102 * Reenable it for this vehicle */
2103 uint16 &gv_flags = this->GetGroundVehicleFlags();
2104 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2105 }
2106 }
2107 }
2108 this->current_order.MakeLoading(false);
2109 }
2110
2111 if (this->last_loading_station != INVALID_STATION &&
2112 this->last_loading_station != this->last_station_visited &&
2113 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2114 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2115 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2116 }
2117
2118 PrepareUnload(this);
2119
2120 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2121 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2122 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2123 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2124
2125 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2126 this->cur_speed = 0;
2127 this->MarkDirty();
2128 }
2129
2135 void Vehicle::CancelReservation(StationID next, Station *st)
2136 {
2137 for (Vehicle *v = this; v != NULL; v = v->next) {
2138 VehicleCargoList &cargo = v->cargo;
2139 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2140 DEBUG(misc, 1, "cancelling cargo reservation");
2141 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2142 cargo.SetTransferLoadPlace(st->xy);
2143 }
2144 cargo.KeepAll();
2145 }
2146 }
2147
2152 void Vehicle::LeaveStation()
2153 {
2154 assert(this->current_order.IsType(OT_LOADING));
2155
2156 delete this->cargo_payment;
2157 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2158
2159 /* Only update the timetable if the vehicle was supposed to stop here. */
2160 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2161
2162 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2163 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2164 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2165 /* Refresh next hop stats to make sure we've done that at least once
2166 * during the stop and that refit_cap == cargo_cap for each vehicle in
2167 * the consist. */
2168 this->ResetRefitCaps();
2169 LinkRefresher::Run(this);
2170
2171 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2172 this->last_loading_station = this->last_station_visited;
2173 } else {
2174 /* if the vehicle couldn't load and had to unload or transfer everything
2175 * set the last loading station to invalid as it will leave empty. */
2176 this->last_loading_station = INVALID_STATION;
2177 }
2178 }
2179
2180 this->current_order.MakeLeaveStation();
2181 Station *st = Station::Get(this->last_station_visited);
2182 this->CancelReservation(INVALID_STATION, st);
2183 st->loading_vehicles.remove(this);
2184
2185 HideFillingPercent(&this->fill_percent_te_id);
2186 trip_occupancy = CalcPercentVehicleFilled(this, NULL);
2187
2188 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2189 /* Trigger station animation (trains only) */
2190 if (IsTileType(this->tile, MP_STATION)) {
2191 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2192 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2193 }
2194
2195 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2196 }
2197
2198 this->MarkDirty();
2199 }
2200
2204 void Vehicle::ResetRefitCaps()
2205 {
2206 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2207 }
2208
2214 void Vehicle::HandleLoading(bool mode)
2215 {
2216 switch (this->current_order.GetType()) {
2217 case OT_LOADING: {
2218 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2219
2220 /* Not the first call for this tick, or still loading */
2221 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2222
2223 this->PlayLeaveStationSound();
2224
2225 this->LeaveStation();
2226
2227 /* Only advance to next order if we just loaded at the current one */
2228 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2229 if (order == NULL ||
2230 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2231 order->GetDestination() != this->last_station_visited) {
2232 return;
2233 }
2234 break;
2235 }
2236
2237 case OT_DUMMY: break;
2238
2239 default: return;
2240 }
2241
2242 this->IncrementImplicitOrderIndex();
2243 }
2244
2249 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2250 {
2251 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2252 if (v->cargo_cap == 0) continue;
2253 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2254 if (pair == capacities.End()) {
2255 pair = capacities.Append();
2256 pair->first = v->cargo_type;
2257 pair->second = v->cargo_cap - v->cargo.StoredCount();
2258 } else {
2259 pair->second += v->cargo_cap - v->cargo.StoredCount();
2260 }
2261 }
2262 }
2263
2264 uint Vehicle::GetConsistTotalCapacity() const
2265 {
2266 uint result = 0;
2267 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2268 result += v->cargo_cap;
2269 }
2270 return result;
2271 }
2272
2279 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2280 {
2281 CommandCost ret = CheckOwnership(this->owner);
2282 if (ret.Failed()) return ret;
2283
2284 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2285 if (this->IsStoppedInDepot()) return CMD_ERROR;
2286
2287 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2288 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2289 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2290 /* We called with a different DEPOT_SERVICE setting.
2291 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2292 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2293 if (flags & DC_EXEC) {
2294 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2295 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2296 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2297 }
2298 return CommandCost();
2299 }
2300
2301 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2302 if (flags & DC_EXEC) {
2303 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2304 * then skip to the next order; effectively cancelling this forced service */
2305 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2306
2307 if (this->IsGroundVehicle()) {
2308 uint16 &gv_flags = this->GetGroundVehicleFlags();
2309 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2310 }
2311
2312 this->current_order.MakeDummy();
2313 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2314 }
2315 return CommandCost();
2316 }
2317
2318 TileIndex location;
2319 DestinationID destination;
2320 bool reverse;
2321 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};
2322 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2323
2324 if (flags & DC_EXEC) {
2325 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2326
2327 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2328 uint16 &gv_flags = this->GetGroundVehicleFlags();
2329 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2330 }
2331
2332 this->dest_tile = location;
2333 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2334 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2335 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2336
2337 /* If there is no depot in front, reverse automatically (trains only) */
2338 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2339
2340 if (this->type == VEH_AIRCRAFT) {
2341 Aircraft *a = Aircraft::From(this);
2342 if (a->state == FLYING && a->targetairport != destination) {
2343 /* The aircraft is now heading for a different hangar than the next in the orders */
2344 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2345 AircraftNextAirportPos_and_Order(a);
2346 }
2347 }
2348 }
2349
2350 return CommandCost();
2351
2352 }
2353
2358 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2359 {
2360 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2361 const Engine *e = this->GetEngine();
2362
2363 /* Evaluate properties */
2364 byte visual_effect;
2365 switch (e->type) {
2366 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2367 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2368 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2369 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2370 }
2371
2372 /* Check powered wagon / visual effect callback */
2373 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2374 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2375
2376 if (callback != CALLBACK_FAILED) {
2377 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2378
2379 callback = GB(callback, 0, 8);
2380 /* Avoid accidentally setting 'visual_effect' to the default value
2381 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2382 if (callback == VE_DEFAULT) {
2383 assert(HasBit(callback, VE_DISABLE_EFFECT));
2384 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2385 }
2386 visual_effect = callback;
2387 }
2388 }
2389
2390 /* Apply default values */
2391 if (visual_effect == VE_DEFAULT ||
2392 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2393 /* Only train engines have default effects.
2394 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2395 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2396 if (visual_effect == VE_DEFAULT) {
2397 visual_effect = 1 << VE_DISABLE_EFFECT;
2398 } else {
2399 SetBit(visual_effect, VE_DISABLE_EFFECT);
2400 }
2401 } else {
2402 if (visual_effect == VE_DEFAULT) {
2403 /* Also set the offset */
2404 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2405 }
2406 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2407 }
2408 }
2409
2410 this->vcache.cached_vis_effect = visual_effect;
2411
2412 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2413 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2414 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2415 }
2416 }
2417
2418 static const int8 _vehicle_smoke_pos[8] = {
2419 1, 1, 1, 0, -1, -1, -1, 0
2420 };
2421
2426 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2427 {
2428 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2429 if (callback == CALLBACK_FAILED) return;
2430
2431 uint count = GB(callback, 0, 2);
2432 bool auto_center = HasBit(callback, 13);
2433 bool auto_rotate = !HasBit(callback, 14);
2434
2435 int8 l_center = 0;
2436 if (auto_center) {
2437 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2438 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2439 } else {
2440 /* For trains: Compute offset from vehicle position to sprite position */
2441 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2442 }
2443
2444 Direction l_dir = v->direction;
2445 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2446 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2447
2448 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2449 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2450
2451 for (uint i = 0; i < count; i++) {
2452 uint32 reg = GetRegister(0x100 + i);
2453 uint type = GB(reg, 0, 8);
2454 int8 x = GB(reg, 8, 8);
2455 int8 y = GB(reg, 16, 8);
2456 int8 z = GB(reg, 24, 8);
2457
2458 if (auto_rotate) {
2459 int8 l = x;
2460 int8 t = y;
2461 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2462 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2463 }
2464
2465 if (type >= 0xF0) {
2466 switch (type) {
2467 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2468 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2469 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2470 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2471 default: break;
2472 }
2473 }
2474 }
2475 }
2476
2481 void Vehicle::ShowVisualEffect() const
2482 {
2483 assert(this->IsPrimaryVehicle());
2484 bool sound = false;
2485
2486 /* Do not show any smoke when:
2487 * - vehicle smoke is disabled by the player
2488 * - the vehicle is slowing down or stopped (by the player)
2489 * - the vehicle is moving very slowly
2490 */
2491 if (_settings_game.vehicle.smoke_amount == 0 ||
2492 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2493 this->cur_speed < 2) {
2494 return;
2495 }
2496
2497 /* Use the speed as limited by underground and orders. */
2498 uint max_speed = this->GetCurrentMaxSpeed();
2499
2500 if (this->type == VEH_TRAIN) {
2501 const Train *t = Train::From(this);
2502 /* For trains, do not show any smoke when:
2503 * - the train is reversing
2504 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2505 */
2506 if (HasBit(t->flags, VRF_REVERSING) ||
2507 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2508 t->cur_speed >= max_speed)) {
2509 return;
2510 }
2511 }
2512
2513 const Vehicle *v = this;
2514
2515 do {
2516 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2517 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2518 VisualEffectSpawnModel effect_model = VESM_NONE;
2519 if (advanced) {
2520 effect_offset = VE_OFFSET_CENTRE;
2521 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2522 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2523 } else {
2524 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2525 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2526 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2527 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2528 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2529 }
2530
2531 /* Show no smoke when:
2532 * - Smoke has been disabled for this vehicle
2533 * - The vehicle is not visible
2534 * - The vehicle is under a bridge
2535 * - The vehicle is on a depot tile
2536 * - The vehicle is on a tunnel tile
2537 * - The vehicle is a train engine that is currently unpowered */
2538 if (effect_model == VESM_NONE ||
2539 v->vehstatus & VS_HIDDEN ||
2540 IsBridgeAbove(v->tile) ||
2541 IsDepotTile(v->tile) ||
2542 IsTunnelTile(v->tile) ||
2543 (v->type == VEH_TRAIN &&
2544 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2545 continue;
2546 }
2547
2548 EffectVehicleType evt = EV_END;
2549 switch (effect_model) {
2550 case VESM_STEAM:
2551 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2552 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2553 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2554 * REGULATION:
2555 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2556 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2557 evt = EV_STEAM_SMOKE;
2558 }
2559 break;
2560
2561 case VESM_DIESEL: {
2562 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2563 * when smoke emission stops.
2564 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2565 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2566 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2567 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2568 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2569 * maximum speed no diesel_smoke is emitted.
2570 * REGULATION:
2571 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2572 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2573 int power_weight_effect = 0;
2574 if (v->type == VEH_TRAIN) {
2575 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2576 }
2577 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2578 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2579 evt = EV_DIESEL_SMOKE;
2580 }
2581 break;
2582 }
2583
2584 case VESM_ELECTRIC:
2585 /* Electric train's spark - more often occurs when train is departing (more load)
2586 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2587 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2588 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2589 * REGULATION:
2590 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2591 if (GB(v->tick_counter, 0, 2) == 0 &&
2592 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2593 evt = EV_ELECTRIC_SPARK;
2594 }
2595 break;
2596
2597 default:
2598 NOT_REACHED();
2599 }
2600
2601 if (evt != EV_END && advanced) {
2602 sound = true;
2603 SpawnAdvancedVisualEffect(v);
2604 } else if (evt != EV_END) {
2605 sound = true;
2606
2607 /* The effect offset is relative to a point 4 units behind the vehicle's
2608 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2609 * correction factor. */
2610 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2611
2612 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2613 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2614
2615 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2616 x = -x;
2617 y = -y;
2618 }
2619
2620 CreateEffectVehicleRel(v, x, y, 10, evt);
2621 }
2622 } while ((v = v->Next()) != NULL);
2623
2624 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2625 }
2626
2631 void Vehicle::SetNext(Vehicle *next)
2632 {
2633 assert(this != next);
2634
2635 if (this->next != NULL) {
2636 /* We had an old next vehicle. Update the first and previous pointers */
2637 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2638 v->first = this->next;
2639 }
2640 this->next->previous = NULL;
2641 }
2642
2643 this->next = next;
2644
2645 if (this->next != NULL) {
2646 /* A new next vehicle. Update the first and previous pointers */
2647 if (this->next->previous != NULL) this->next->previous->next = NULL;
2648 this->next->previous = this;
2649 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2650 v->first = this->first;
2651 }
2652 }
2653 }
2654
2660 void Vehicle::AddToShared(Vehicle *shared_chain)
2661 {
2662 assert(this->previous_shared == NULL && this->next_shared == NULL);
2663
2664 if (shared_chain->orders.list == NULL) {
2665 assert(shared_chain->previous_shared == NULL);
2666 assert(shared_chain->next_shared == NULL);
2667 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2668 }
2669
2670 this->next_shared = shared_chain->next_shared;
2671 this->previous_shared = shared_chain;
2672
2673 shared_chain->next_shared = this;
2674
2675 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2676
2677 shared_chain->orders.list->AddVehicle(this);
2678 }
2679
2683 void Vehicle::RemoveFromShared()
2684 {
2685 /* Remember if we were first and the old window number before RemoveVehicle()
2686 * as this changes first if needed. */
2687 bool were_first = (this->FirstShared() == this);
2688 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2689
2690 this->orders.list->RemoveVehicle(this);
2691
2692 if (!were_first) {
2693 /* We are not the first shared one, so only relink our previous one. */
2694 this->previous_shared->next_shared = this->NextShared();
2695 }
2696
2697 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2698
2699
2700 if (this->orders.list->GetNumVehicles() == 1) {
2701 /* When there is only one vehicle, remove the shared order list window. */
2702 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2703 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
2704 } else if (were_first) {
2705 /* If we were the first one, update to the new first one.
2706 * Note: FirstShared() is already the new first */
2707 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2708 }
2709
2710 this->next_shared = NULL;
2711 this->previous_shared = NULL;
2712 }
2713
2714 void VehiclesYearlyLoop()
2715 {
2716 Vehicle *v;
2717 FOR_ALL_VEHICLES(v) {
2718 if (v->IsPrimaryVehicle()) {
2719 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2720 Money profit = v->GetDisplayProfitThisYear();
2721 if (v->age >= 730 && profit < 0) {
2722 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2723 SetDParam(0, v->index);
2724 SetDParam(1, profit);
2725 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2726 }
2727 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2728 }
2729
2730 v->profit_last_year = v->profit_this_year;
2731 v->profit_this_year = 0;
2732 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2733 }
2734 }
2735 GroupStatistics::UpdateProfits();
2736 SetWindowClassesDirty(WC_TRAINS_LIST);
2737 SetWindowClassesDirty(WC_SHIPS_LIST);
2738 SetWindowClassesDirty(WC_ROADVEH_LIST);
2739 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2740 }
2741
2742
2752 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2753 {
2754 const Engine *e = Engine::GetIfValid(engine_type);
2755 assert(e != NULL);
2756
2757 switch (e->type) {
2758 case VEH_TRAIN:
2759 return (st->facilities & FACIL_TRAIN) != 0;
2760
2761 case VEH_ROAD:
2762 /* For road vehicles we need the vehicle to know whether it can actually
2763 * use the station, but if it doesn't have facilities for RVs it is
2764 * certainly not possible that the station can be used. */
2765 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2766
2767 case VEH_SHIP:
2768 return (st->facilities & FACIL_DOCK) != 0;
2769
2770 case VEH_AIRCRAFT:
2771 return (st->facilities & FACIL_AIRPORT) != 0 &&
2772 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2773
2774 default:
2775 return false;
2776 }
2777 }
2778
2785 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2786 {
2787 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2788
2789 return CanVehicleUseStation(v->engine_type, st);
2790 }
2791
2797 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2798 {
2799 assert(this->IsGroundVehicle());
2800 if (this->type == VEH_TRAIN) {
2801 return &Train::From(this)->gcache;
2802 } else {
2803 return &RoadVehicle::From(this)->gcache;
2804 }
2805 }
2806
2812 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2813 {
2814 assert(this->IsGroundVehicle());
2815 if (this->type == VEH_TRAIN) {
2816 return &Train::From(this)->gcache;
2817 } else {
2818 return &RoadVehicle::From(this)->gcache;
2819 }
2820 }
2821
2827 uint16 &Vehicle::GetGroundVehicleFlags()
2828 {
2829 assert(this->IsGroundVehicle());
2830 if (this->type == VEH_TRAIN) {
2831 return Train::From(this)->gv_flags;
2832 } else {
2833 return RoadVehicle::From(this)->gv_flags;
2834 }
2835 }
2836
2842 const uint16 &Vehicle::GetGroundVehicleFlags() const
2843 {
2844 assert(this->IsGroundVehicle());
2845 if (this->type == VEH_TRAIN) {
2846 return Train::From(this)->gv_flags;
2847 } else {
2848 return RoadVehicle::From(this)->gv_flags;
2849 }
2850 }
2851
2860 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2861 {
2862 if (v->type == VEH_TRAIN) {
2863 Train *u = Train::From(v);
2864 /* Only include whole vehicles, so start with the first articulated part */
2865 u = u->GetFirstEnginePart();
2866
2867 /* Include num_vehicles vehicles, not counting articulated parts */
2868 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2869 do {
2870 /* Include current vehicle in the selection. */
2871 set.Include(u->index);
2872
2873 /* If the vehicle is multiheaded, add the other part too. */
2874 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2875
2876 u = u->Next();
2877 } while (u != NULL && u->IsArticulatedPart());
2878 }
2879 }
2880 }