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 27584 2016-05-29 13:33:20Z 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 #define GEN_HASH(x, y) ((GB((y), 6 + ZOOM_LVL_SHIFT, 6) << 6) + GB((x), 7 + ZOOM_LVL_SHIFT, 6))
61
62 VehicleID _new_vehicle_id;
63 uint16 _returned_refit_capacity;
64 uint16 _returned_mail_refit_capacity;
65
66
68 VehiclePool _vehicle_pool("Vehicle");
69 INSTANTIATE_POOL_METHODS(Vehicle)
70
71
77 bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
78 {
79 /* We can always generate the Company pointer when we have the vehicle.
80 * However this takes time and since the Company pointer is often present
81 * when this function is called then it's faster to pass the pointer as an
82 * argument rather than finding it again. */
83 assert(c == Company::Get(this->owner));
84
85 if (use_renew_setting && !c->settings.engine_renew) return false;
86 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
87
88 /* Only engines need renewing */
89 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
90
91 return true;
92 }
93
99 void VehicleServiceInDepot(Vehicle *v)
100 {
101 assert(v != NULL);
102 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
103
104 do {
105 v->date_of_last_service = _date;
106 v->breakdowns_since_last_service = 0;
107 v->reliability = v->GetEngine()->reliability;
108 /* Prevent vehicles from breaking down directly after exiting the depot. */
109 v->breakdown_chance /= 4;
110 v = v->Next();
111 } while (v != NULL && v->HasEngineType());
112 }
113
120 bool Vehicle::NeedsServicing() const
121 {
122 /* Stopped or crashed vehicles will not move, as such making unmovable
123 * vehicles to go for service is lame. */
124 if (this->vehstatus & (VS_STOPPED | VS_CRASHED)) return false;
125
126 /* Are we ready for the next service cycle? */
127 const Company *c = Company::Get(this->owner);
128 if (this->ServiceIntervalIsPercent() ?
129 (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) :
130 (this->date_of_last_service + this->GetServiceInterval() >= _date)) {
131 return false;
132 }
133
134 /* If we're servicing anyway, because we have not disabled servicing when
135 * there are no breakdowns or we are playing with breakdowns, bail out. */
136 if (!_settings_game.order.no_servicing_if_no_breakdowns ||
137 _settings_game.difficulty.vehicle_breakdowns != 0) {
138 return true;
139 }
140
141 /* Test whether there is some pending autoreplace.
142 * Note: We do this after the service-interval test.
143 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
144 bool pending_replace = false;
145 Money needed_money = c->settings.engine_renew_money;
146 if (needed_money > c->money) return false;
147
148 for (const Vehicle *v = this; v != NULL; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : NULL) {
149 bool replace_when_old = false;
150 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
151
152 /* Check engine availability */
153 if (new_engine == INVALID_ENGINE || !HasBit(Engine::Get(new_engine)->company_avail, v->owner)) continue;
154 /* Is the vehicle old if we are not always replacing? */
155 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
156
157 /* Check refittability */
158 uint32 available_cargo_types, union_mask;
159 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
160 /* Is there anything to refit? */
161 if (union_mask != 0) {
162 CargoID cargo_type;
163 /* We cannot refit to mixed cargoes in an automated way */
164 if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
165
166 /* Did the old vehicle carry anything? */
167 if (cargo_type != CT_INVALID) {
168 /* We can't refit the vehicle to carry the cargo we want */
169 if (!HasBit(available_cargo_types, cargo_type)) continue;
170 }
171 }
172
173 /* Check money.
174 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
175 pending_replace = true;
176 needed_money += 2 * Engine::Get(new_engine)->GetCost();
177 if (needed_money > c->money) return false;
178 }
179
180 return pending_replace;
181 }
182
188 bool Vehicle::NeedsAutomaticServicing() const
189 {
190 if (this->HasDepotOrder()) return false;
191 if (this->current_order.IsType(OT_LOADING)) return false;
192 if (this->current_order.IsType(OT_GOTO_DEPOT) && this->current_order.GetDepotOrderType() != ODTFB_SERVICE) return false;
193 return NeedsServicing();
194 }
195
196 uint Vehicle::Crash(bool flooded)
197 {
198 assert((this->vehstatus & VS_CRASHED) == 0);
199 assert(this->Previous() == NULL); // IsPrimaryVehicle fails for free-wagon-chains
200
201 uint pass = 0;
202 /* Stop the vehicle. */
203 if (this->IsPrimaryVehicle()) this->vehstatus |= VS_STOPPED;
204 /* crash all wagons, and count passengers */
205 for (Vehicle *v = this; v != NULL; v = v->Next()) {
206 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
207 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.TotalCount();
208 v->vehstatus |= VS_CRASHED;
209 v->MarkAllViewportsDirty();
210 }
211
212 /* Dirty some windows */
213 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
214 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
215 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
216 SetWindowDirty(WC_VEHICLE_DEPOT, this->tile);
217
218 delete this->cargo_payment;
219 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
220
221 return RandomRange(pass + 1); // Randomise deceased passengers.
222 }
223
224
233 void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
234 {
235 const Engine *e = Engine::Get(engine);
236 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
237
238 /* Missing GRF. Nothing useful can be done in this situation. */
239 if (grfconfig == NULL) return;
240
241 if (!HasBit(grfconfig->grf_bugs, bug_type)) {
242 SetBit(grfconfig->grf_bugs, bug_type);
243 SetDParamStr(0, grfconfig->GetName());
244 SetDParam(1, engine);
245 ShowErrorMessage(part1, part2, WL_CRITICAL);
246 if (!_networking) DoCommand(0, critical ? PM_PAUSED_ERROR : PM_PAUSED_NORMAL, 1, DC_EXEC, CMD_PAUSE);
247 }
248
249 /* debug output */
250 char buffer[512];
251
252 SetDParamStr(0, grfconfig->GetName());
253 GetString(buffer, part1, lastof(buffer));
254 DEBUG(grf, 0, "%s", buffer + 3);
255
256 SetDParam(1, engine);
257 GetString(buffer, part2, lastof(buffer));
258 DEBUG(grf, 0, "%s", buffer + 3);
259 }
260
266 void VehicleLengthChanged(const Vehicle *u)
267 {
268 /* show a warning once for each engine in whole game and once for each GRF after each game load */
269 const Engine *engine = u->GetEngine();
270 uint32 grfid = engine->grf_prop.grffile->grfid;
271 GRFConfig *grfconfig = GetGRFConfig(grfid);
272 if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
273 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
274 }
275 }
276
281 Vehicle::Vehicle(VehicleType type)
282 {
283 this->type = type;
284 this->coord.left = INVALID_COORD;
285 this->group_id = DEFAULT_GROUP;
286 this->fill_percent_te_id = INVALID_TE_ID;
287 this->first = this;
288 this->colourmap = PAL_NONE;
289 this->cargo_age_counter = 1;
290 this->last_station_visited = INVALID_STATION;
291 this->last_loading_station = INVALID_STATION;
292 }
293
298 byte VehicleRandomBits()
299 {
300 return GB(Random(), 0, 8);
301 }
302
303 /* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
304 * lookup times at the expense of memory usage. */
305 const int HASH_BITS = 7;
306 const int HASH_SIZE = 1 << HASH_BITS;
307 const int HASH_MASK = HASH_SIZE - 1;
308 const int TOTAL_HASH_SIZE = 1 << (HASH_BITS * 2);
309 const int TOTAL_HASH_MASK = TOTAL_HASH_SIZE - 1;
310
311 /* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
312 * Profiling results show that 0 is fastest. */
313 const int HASH_RES = 0;
314
315 static Vehicle *_vehicle_tile_hash[TOTAL_HASH_SIZE];
316
317 static Vehicle *VehicleFromTileHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first)
318 {
319 for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) {
320 for (int x = xl; ; x = (x + 1) & HASH_MASK) {
321 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
322 for (; v != NULL; v = v->hash_tile_next) {
323 Vehicle *a = proc(v, data);
324 if (find_first && a != NULL) return a;
325 }
326 if (x == xu) break;
327 }
328 if (y == yu) break;
329 }
330
331 return NULL;
332 }
333
334
346 static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
347 {
348 const int COLL_DIST = 6;
349
350 /* Hash area to scan is from xl,yl to xu,yu */
351 int xl = GB((x - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
352 int xu = GB((x + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS);
353 int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
354 int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS;
355
356 return VehicleFromTileHash(xl, yl, xu, yu, data, proc, find_first);
357 }
358
373 void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
374 {
375 VehicleFromPosXY(x, y, data, proc, false);
376 }
377
389 bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
390 {
391 return VehicleFromPosXY(x, y, data, proc, true) != NULL;
392 }
393
404 static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first)
405 {
406 int x = GB(TileX(tile), HASH_RES, HASH_BITS);
407 int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS;
408
409 Vehicle *v = _vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
410 for (; v != NULL; v = v->hash_tile_next) {
411 if (v->tile != tile) continue;
412
413 Vehicle *a = proc(v, data);
414 if (find_first && a != NULL) return a;
415 }
416
417 return NULL;
418 }
419
433 void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
434 {
435 VehicleFromPos(tile, data, proc, false);
436 }
437
448 bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
449 {
450 return VehicleFromPos(tile, data, proc, true) != NULL;
451 }
452
459 static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data)
460 {
461 int z = *(int*)data;
462
463 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
464 if (v->z_pos > z) return NULL;
465
466 return v;
467 }
468
474 CommandCost EnsureNoVehicleOnGround(TileIndex tile)
475 {
476 int z = GetTileMaxPixelZ(tile);
477
478 /* Value v is not safe in MP games, however, it is used to generate a local
479 * error message only (which may be different for different machines).
480 * Such a message does not affect MP synchronisation.
481 */
482 Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ, true);
483 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
484 return CommandCost();
485 }
486
488 static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
489 {
490 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) return NULL;
491 if (v == (const Vehicle *)data) return NULL;
492
493 return v;
494 }
495
503 CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
504 {
505 /* Value v is not safe in MP games, however, it is used to generate a local
506 * error message only (which may be different for different machines).
507 * Such a message does not affect MP synchronisation.
508 */
509 Vehicle *v = VehicleFromPos(tile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
510 if (v == NULL) v = VehicleFromPos(endtile, const_cast<Vehicle *>(ignore), &GetVehicleTunnelBridgeProc, true);
511
512 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
513 return CommandCost();
514 }
515
516 static Vehicle *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
517 {
518 TrackBits rail_bits = *(TrackBits *)data;
519
520 if (v->type != VEH_TRAIN) return NULL;
521
522 Train *t = Train::From(v);
523 if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return NULL;
524
525 return v;
526 }
527
536 CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
537 {
538 /* Value v is not safe in MP games, however, it is used to generate a local
539 * error message only (which may be different for different machines).
540 * Such a message does not affect MP synchronisation.
541 */
542 Vehicle *v = VehicleFromPos(tile, &track_bits, &EnsureNoTrainOnTrackProc, true);
543 if (v != NULL) return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
544 return CommandCost();
545 }
546
547 static void UpdateVehicleTileHash(Vehicle *v, bool remove)
548 {
549 Vehicle **old_hash = v->hash_tile_current;
550 Vehicle **new_hash;
551
552 if (remove) {
553 new_hash = NULL;
554 } else {
555 int x = GB(TileX(v->tile), HASH_RES, HASH_BITS);
556 int y = GB(TileY(v->tile), HASH_RES, HASH_BITS) << HASH_BITS;
557 new_hash = &_vehicle_tile_hash[(x + y) & TOTAL_HASH_MASK];
558 }
559
560 if (old_hash == new_hash) return;
561
562 /* Remove from the old position in the hash table */
563 if (old_hash != NULL) {
564 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = v->hash_tile_prev;
565 *v->hash_tile_prev = v->hash_tile_next;
566 }
567
568 /* Insert vehicle at beginning of the new position in the hash table */
569 if (new_hash != NULL) {
570 v->hash_tile_next = *new_hash;
571 if (v->hash_tile_next != NULL) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
572 v->hash_tile_prev = new_hash;
573 *new_hash = v;
574 }
575
576 /* Remember current hash position */
577 v->hash_tile_current = new_hash;
578 }
579
580 static Vehicle *_vehicle_viewport_hash[0x1000];
581
582 static void UpdateVehicleViewportHash(Vehicle *v, int x, int y)
583 {
584 Vehicle **old_hash, **new_hash;
585 int old_x = v->coord.left;
586 int old_y = v->coord.top;
587
588 new_hash = (x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(x, y)];
589 old_hash = (old_x == INVALID_COORD) ? NULL : &_vehicle_viewport_hash[GEN_HASH(old_x, old_y)];
590
591 if (old_hash == new_hash) return;
592
593 /* remove from hash table? */
594 if (old_hash != NULL) {
595 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = v->hash_viewport_prev;
596 *v->hash_viewport_prev = v->hash_viewport_next;
597 }
598
599 /* insert into hash table? */
600 if (new_hash != NULL) {
601 v->hash_viewport_next = *new_hash;
602 if (v->hash_viewport_next != NULL) v->hash_viewport_next->hash_viewport_prev = &v->hash_viewport_next;
603 v->hash_viewport_prev = new_hash;
604 *new_hash = v;
605 }
606 }
607
608 void ResetVehicleHash()
609 {
610 Vehicle *v;
611 FOR_ALL_VEHICLES(v) { v->hash_tile_current = NULL; }
612 memset(_vehicle_viewport_hash, 0, sizeof(_vehicle_viewport_hash));
613 memset(_vehicle_tile_hash, 0, sizeof(_vehicle_tile_hash));
614 }
615
616 void ResetVehicleColourMap()
617 {
618 Vehicle *v;
619 FOR_ALL_VEHICLES(v) { v->colourmap = PAL_NONE; }
620 }
621
626 typedef SmallMap<Vehicle *, bool, 4> AutoreplaceMap;
627 static AutoreplaceMap _vehicles_to_autoreplace;
628
629 void InitializeVehicles()
630 {
631 _vehicles_to_autoreplace.Reset();
632 ResetVehicleHash();
633 }
634
635 uint CountVehiclesInChain(const Vehicle *v)
636 {
637 uint count = 0;
638 do count++; while ((v = v->Next()) != NULL);
639 return count;
640 }
641
646 bool Vehicle::IsEngineCountable() const
647 {
648 switch (this->type) {
649 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
650 case VEH_TRAIN:
651 return !this->IsArticulatedPart() && // tenders and other articulated parts
652 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
653 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
654 case VEH_SHIP: return true;
655 default: return false; // Only count company buildable vehicles
656 }
657 }
658
663 bool Vehicle::HasEngineType() const
664 {
665 switch (this->type) {
666 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
667 case VEH_TRAIN:
668 case VEH_ROAD:
669 case VEH_SHIP: return true;
670 default: return false;
671 }
672 }
673
679 const Engine *Vehicle::GetEngine() const
680 {
681 return Engine::Get(this->engine_type);
682 }
683
689 const GRFFile *Vehicle::GetGRF() const
690 {
691 return this->GetEngine()->GetGRF();
692 }
693
699 uint32 Vehicle::GetGRFID() const
700 {
701 return this->GetEngine()->GetGRFID();
702 }
703
711 void Vehicle::HandlePathfindingResult(bool path_found)
712 {
713 if (path_found) {
714 /* Route found, is the vehicle marked with "lost" flag? */
715 if (!HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
716
717 /* Clear the flag as the PF's problem was solved. */
718 ClrBit(this->vehicle_flags, VF_PATHFINDER_LOST);
719 /* Delete the news item. */
720 DeleteVehicleNews(this->index, STR_NEWS_VEHICLE_IS_LOST);
721 return;
722 }
723
724 /* Were we already lost? */
725 if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
726
727 /* It is first time the problem occurred, set the "lost" flag. */
728 SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
729 /* Notify user about the event. */
730 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
731 if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
732 SetDParam(0, this->index);
733 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
734 }
735 }
736
738 void Vehicle::PreDestructor()
739 {
740 if (CleaningPool()) return;
741
742 if (Station::IsValidID(this->last_station_visited)) {
743 Station *st = Station::Get(this->last_station_visited);
744 st->loading_vehicles.remove(this);
745
746 HideFillingPercent(&this->fill_percent_te_id);
747 this->CancelReservation(INVALID_STATION, st);
748 delete this->cargo_payment;
749 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
750 }
751
752 if (this->IsEngineCountable()) {
753 GroupStatistics::CountEngine(this, -1);
754 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
755 GroupStatistics::UpdateAutoreplace(this->owner);
756
757 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
758 DeleteGroupHighlightOfVehicle(this);
759 }
760
761 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
762 Aircraft *a = Aircraft::From(this);
763 Station *st = GetTargetAirportIfValid(a);
764 if (st != NULL) {
765 const AirportFTA *layout = st->airport.GetFTA()->layout;
766 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
767 }
768 }
769
770
771 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
772 RoadVehicle *v = RoadVehicle::From(this);
773 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
774 /* Leave the drive through roadstop, when you have not already left it. */
775 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
776 }
777 }
778
779 if (this->Previous() == NULL) {
780 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
781 }
782
783 if (this->IsPrimaryVehicle()) {
784 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
785 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
786 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
787 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
788 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
789 SetWindowDirty(WC_COMPANY, this->owner);
790 OrderBackup::ClearVehicle(this);
791 }
792 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
793
794 this->cargo.Truncate();
795 DeleteVehicleOrders(this);
796 DeleteDepotHighlightOfVehicle(this);
797
798 extern void StopGlobalFollowVehicle(const Vehicle *v);
799 StopGlobalFollowVehicle(this);
800
801 ReleaseDisastersTargetingVehicle(this->index);
802 }
803
804 Vehicle::~Vehicle()
805 {
806 if (CleaningPool()) {
807 this->cargo.OnCleanPool();
808 return;
809 }
810
811 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
812 * it may happen that vehicle chain is deleted when visible */
813 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
814
815 Vehicle *v = this->Next();
816 this->SetNext(NULL);
817
818 delete v;
819
820 UpdateVehicleTileHash(this, true);
821 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
822 DeleteVehicleNews(this->index, INVALID_STRING_ID);
823 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
824 }
825
830 void VehicleEnteredDepotThisTick(Vehicle *v)
831 {
832 /* Vehicle should stop in the depot if it was in 'stopping' state */
833 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
834
835 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
836 * stopping in the depot, so we stop it to ensure that it will not reserve
837 * the path out of the depot before we might autoreplace it to a different
838 * engine. The new engine would not own the reserved path we store that we
839 * stopped the vehicle, so autoreplace can start it again */
840 v->vehstatus |= VS_STOPPED;
841 }
842
848 static void RunVehicleDayProc()
849 {
850 if (_game_mode != GM_NORMAL) return;
851
852 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
853 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
854 Vehicle *v = Vehicle::Get(i);
855 if (v == NULL) continue;
856
857 /* Call the 32-day callback if needed */
858 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
859 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
860 if (callback != CALLBACK_FAILED) {
861 if (HasBit(callback, 0)) {
862 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
863 }
864
865 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
866 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
867 if (callback != 0) v->First()->MarkDirty();
868
869 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
870 }
871 }
872
873 /* This is called once per day for each vehicle, but not in the first tick of the day */
874 v->OnNewDay();
875 }
876 }
877
878 void CallVehicleTicks()
879 {
880 _vehicles_to_autoreplace.Clear();
881
882 RunVehicleDayProc();
883
884 Station *st;
885 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
886
887 Vehicle *v;
888 FOR_ALL_VEHICLES(v) {
889 /* Vehicle could be deleted in this tick */
890 if (!v->Tick()) {
891 assert(Vehicle::Get(vehicle_index) == NULL);
892 continue;
893 }
894
895 assert(Vehicle::Get(vehicle_index) == v);
896
897 switch (v->type) {
898 default: break;
899
900 case VEH_TRAIN:
901 case VEH_ROAD:
902 case VEH_AIRCRAFT:
903 case VEH_SHIP: {
904 Vehicle *front = v->First();
905
906 if (v->vcache.cached_cargo_age_period != 0) {
907 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
908 if (--v->cargo_age_counter == 0) {
909 v->cargo.AgeCargo();
910 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
911 }
912 }
913
914 /* Do not play any sound when crashed */
915 if (front->vehstatus & VS_CRASHED) continue;
916
917 /* Do not play any sound when in depot or tunnel */
918 if (v->vehstatus & VS_HIDDEN) continue;
919
920 /* Do not play any sound when stopped */
921 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
922
923 /* Check vehicle type specifics */
924 switch (v->type) {
925 case VEH_TRAIN:
926 if (Train::From(v)->IsWagon()) continue;
927 break;
928
929 case VEH_ROAD:
930 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
931 break;
932
933 case VEH_AIRCRAFT:
934 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
935 break;
936
937 default:
938 break;
939 }
940
941 v->motion_counter += front->cur_speed;
942 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
943 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
944
945 /* Play an alternating running sound every 16 ticks */
946 if (GB(v->tick_counter, 0, 4) == 0) {
947 /* Play running sound when speed > 0 and not braking */
948 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
949 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
950 }
951
952 break;
953 }
954 }
955 }
956
957 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
958 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
959 v = it->first;
960 /* Autoreplace needs the current company set as the vehicle owner */
961 cur_company.Change(v->owner);
962
963 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
964 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
965 * they are already leaving the depot again before being replaced. */
966 if (it->second) v->vehstatus &= ~VS_STOPPED;
967
968 /* Store the position of the effect as the vehicle pointer will become invalid later */
969 int x = v->x_pos;
970 int y = v->y_pos;
971 int z = v->z_pos;
972
973 const Company *c = Company::Get(_current_company);
974 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
975 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
976 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
977
978 if (!IsLocalCompany()) continue;
979
980 if (res.Succeeded()) {
981 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
982 continue;
983 }
984
985 StringID error_message = res.GetErrorMessage();
986 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
987
988 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
989
990 StringID message;
991 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
992 message = error_message;
993 } else {
994 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
995 }
996
997 SetDParam(0, v->index);
998 SetDParam(1, error_message);
999 AddVehicleAdviceNewsItem(message, v->index);
1000 }
1001
1002 cur_company.Restore();
1003 }
1004
1009 static void DoDrawVehicle(const Vehicle *v)
1010 {
1011 SpriteID image = v->cur_image;
1012 PaletteID pal = PAL_NONE;
1013
1014 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1015
1016 /* Check whether the vehicle shall be transparent due to the game state */
1017 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1018
1019 if (v->type == VEH_EFFECT) {
1020 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1021 * However, transparent smoke and bubbles look weird, so always hide them. */
1022 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1023 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1024 }
1025
1026 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1027 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1028 }
1029
1034 void ViewportAddVehicles(DrawPixelInfo *dpi)
1035 {
1036 /* The bounding rectangle */
1037 const int l = dpi->left;
1038 const int r = dpi->left + dpi->width;
1039 const int t = dpi->top;
1040 const int b = dpi->top + dpi->height;
1041
1042 /* The hash area to scan */
1043 int xl, xu, yl, yu;
1044
1045 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
1046 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
1047 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
1048 } else {
1049 /* scan whole hash row */
1050 xl = 0;
1051 xu = 0x3F;
1052 }
1053
1054 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
1055 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
1056 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
1057 } else {
1058 /* scan whole column */
1059 yl = 0;
1060 yu = 0x3F << 6;
1061 }
1062
1063 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1064 for (int x = xl;; x = (x + 1) & 0x3F) {
1065 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1066
1067 while (v != NULL) {
1068 if (!(v->vehstatus & VS_HIDDEN) &&
1069 l <= v->coord.right &&
1070 t <= v->coord.bottom &&
1071 r >= v->coord.left &&
1072 b >= v->coord.top) {
1073 DoDrawVehicle(v);
1074 }
1075 v = v->hash_viewport_next;
1076 }
1077
1078 if (x == xu) break;
1079 }
1080
1081 if (y == yu) break;
1082 }
1083 }
1084
1092 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1093 {
1094 Vehicle *found = NULL, *v;
1095 uint dist, best_dist = UINT_MAX;
1096
1097 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1098
1099 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1100 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1101
1102 FOR_ALL_VEHICLES(v) {
1103 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1104 x >= v->coord.left && x <= v->coord.right &&
1105 y >= v->coord.top && y <= v->coord.bottom) {
1106
1107 dist = max(
1108 abs(((v->coord.left + v->coord.right) >> 1) - x),
1109 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1110 );
1111
1112 if (dist < best_dist) {
1113 found = v;
1114 best_dist = dist;
1115 }
1116 }
1117 }
1118
1119 return found;
1120 }
1121
1126 void DecreaseVehicleValue(Vehicle *v)
1127 {
1128 v->value -= v->value >> 8;
1129 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1130 }
1131
1132 static const byte _breakdown_chance[64] = {
1133 3, 3, 3, 3, 3, 3, 3, 3,
1134 4, 4, 5, 5, 6, 6, 7, 7,
1135 8, 8, 9, 9, 10, 10, 11, 11,
1136 12, 13, 13, 13, 13, 14, 15, 16,
1137 17, 19, 21, 25, 28, 31, 34, 37,
1138 40, 44, 48, 52, 56, 60, 64, 68,
1139 72, 80, 90, 100, 110, 120, 130, 140,
1140 150, 170, 190, 210, 230, 250, 250, 250,
1141 };
1142
1143 void CheckVehicleBreakdown(Vehicle *v)
1144 {
1145 int rel, rel_old;
1146
1147 /* decrease reliability */
1148 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1149 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1150
1151 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1152 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1153 v->cur_speed < 5 || _game_mode == GM_MENU) {
1154 return;
1155 }
1156
1157 uint32 r = Random();
1158
1159 /* increase chance of failure */
1160 int chance = v->breakdown_chance + 1;
1161 if (Chance16I(1, 25, r)) chance += 25;
1162 v->breakdown_chance = min(255, chance);
1163
1164 /* calculate reliability value to use in comparison */
1165 rel = v->reliability;
1166 if (v->type == VEH_SHIP) rel += 0x6666;
1167
1168 /* reduced breakdowns? */
1169 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1170
1171 /* check if to break down */
1172 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1173 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1174 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1175 v->breakdown_chance = 0;
1176 }
1177 }
1178
1185 bool Vehicle::HandleBreakdown()
1186 {
1187 /* Possible states for Vehicle::breakdown_ctr
1188 * 0 - vehicle is running normally
1189 * 1 - vehicle is currently broken down
1190 * 2 - vehicle is going to break down now
1191 * >2 - vehicle is counting down to the actual breakdown event */
1192 switch (this->breakdown_ctr) {
1193 case 0:
1194 return false;
1195
1196 case 2:
1197 this->breakdown_ctr = 1;
1198
1199 if (this->breakdowns_since_last_service != 255) {
1200 this->breakdowns_since_last_service++;
1201 }
1202
1203 if (this->type == VEH_AIRCRAFT) {
1204 /* Aircraft just need this flag, the rest is handled elsewhere */
1205 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1206 } else {
1207 this->cur_speed = 0;
1208
1209 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1210 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1211 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1212 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1213 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1214 }
1215
1216 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1217 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1218 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1219 }
1220 }
1221
1222 this->MarkDirty(); // Update graphics after speed is zeroed
1223 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1224 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1225
1226 /* FALL THROUGH */
1227 case 1:
1228 /* Aircraft breakdowns end only when arriving at the airport */
1229 if (this->type == VEH_AIRCRAFT) return false;
1230
1231 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1232 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1233 if (--this->breakdown_delay == 0) {
1234 this->breakdown_ctr = 0;
1235 this->MarkDirty();
1236 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1237 }
1238 }
1239 return true;
1240
1241 default:
1242 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1243 return false;
1244 }
1245 }
1246
1251 void AgeVehicle(Vehicle *v)
1252 {
1253 if (v->age < MAX_DAY) {
1254 v->age++;
1255 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1256 }
1257
1258 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1259
1260 int age = v->age - v->max_age;
1261 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1262 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1263 v->reliability_spd_dec <<= 1;
1264 }
1265
1266 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1267
1268 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1269 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1270
1271 /* Don't warn if a renew is active */
1272 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1273
1274 StringID str;
1275 if (age == -DAYS_IN_LEAP_YEAR) {
1276 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1277 } else if (age == 0) {
1278 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1279 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1280 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1281 } else {
1282 return;
1283 }
1284
1285 SetDParam(0, v->index);
1286 AddVehicleAdviceNewsItem(str, v->index);
1287 }
1288
1298 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1299 {
1300 int count = 0;
1301 int max = 0;
1302 int cars = 0;
1303 int unloading = 0;
1304 bool loading = false;
1305
1306 bool is_loading = front->current_order.IsType(OT_LOADING);
1307
1308 /* The station may be NULL when the (colour) string does not need to be set. */
1309 const Station *st = Station::GetIfValid(front->last_station_visited);
1310 assert(colour == NULL || (st != NULL && is_loading));
1311
1312 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1313 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1314
1315 /* Count up max and used */
1316 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1317 count += v->cargo.StoredCount();
1318 max += v->cargo_cap;
1319 if (v->cargo_cap != 0 && colour != NULL) {
1320 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1321 loading |= !order_no_load &&
1322 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1323 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1324 cars++;
1325 }
1326 }
1327
1328 if (colour != NULL) {
1329 if (unloading == 0 && loading) {
1330 *colour = STR_PERCENT_UP;
1331 } else if (unloading == 0 && !loading) {
1332 *colour = STR_PERCENT_NONE;
1333 } else if (cars == unloading || !loading) {
1334 *colour = STR_PERCENT_DOWN;
1335 } else {
1336 *colour = STR_PERCENT_UP_DOWN;
1337 }
1338 }
1339
1340 /* Train without capacity */
1341 if (max == 0) return 100;
1342
1343 /* Return the percentage */
1344 if (count * 2 < max) {
1345 /* Less than 50%; round up, so that 0% means really empty. */
1346 return CeilDiv(count * 100, max);
1347 } else {
1348 /* More than 50%; round down, so that 100% means really full. */
1349 return (count * 100) / max;
1350 }
1351 }
1352
1357 void VehicleEnterDepot(Vehicle *v)
1358 {
1359 /* Always work with the front of the vehicle */
1360 assert(v == v->First());
1361
1362 switch (v->type) {
1363 case VEH_TRAIN: {
1364 Train *t = Train::From(v);
1365 SetWindowClassesDirty(WC_TRAINS_LIST);
1366 /* Clear path reservation */
1367 SetDepotReservation(t->tile, false);
1368 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1369
1370 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1371 t->wait_counter = 0;
1372 t->force_proceed = TFP_NONE;
1373 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1374 t->ConsistChanged(CCF_ARRANGE);
1375 break;
1376 }
1377
1378 case VEH_ROAD:
1379 SetWindowClassesDirty(WC_ROADVEH_LIST);
1380 break;
1381
1382 case VEH_SHIP: {
1383 SetWindowClassesDirty(WC_SHIPS_LIST);
1384 Ship *ship = Ship::From(v);
1385 ship->state = TRACK_BIT_DEPOT;
1386 ship->UpdateCache();
1387 ship->UpdateViewport(true, true);
1388 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1389 break;
1390 }
1391
1392 case VEH_AIRCRAFT:
1393 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1394 HandleAircraftEnterHangar(Aircraft::From(v));
1395 break;
1396 default: NOT_REACHED();
1397 }
1398 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1399
1400 if (v->type != VEH_TRAIN) {
1401 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1402 * 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 */
1403 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1404 }
1405 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1406
1407 v->vehstatus |= VS_HIDDEN;
1408 v->cur_speed = 0;
1409
1410 VehicleServiceInDepot(v);
1411
1412 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1413 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1414 v->MarkDirty();
1415
1416 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1417 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1418
1419 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1420
1421 /* Test whether we are heading for this depot. If not, do nothing.
1422 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1423 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1424 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1425 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1426 /* We are heading for another depot, keep driving. */
1427 return;
1428 }
1429
1430 if (v->current_order.IsRefit()) {
1431 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1432 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1433 cur_company.Restore();
1434
1435 if (cost.Failed()) {
1436 _vehicles_to_autoreplace[v] = false;
1437 if (v->owner == _local_company) {
1438 /* Notify the user that we stopped the vehicle */
1439 SetDParam(0, v->index);
1440 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1441 }
1442 } else if (cost.GetCost() != 0) {
1443 v->profit_this_year -= cost.GetCost() << 8;
1444 if (v->owner == _local_company) {
1445 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1446 }
1447 }
1448 }
1449
1450 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1451 /* Part of orders */
1452 v->DeleteUnreachedImplicitOrders();
1453 UpdateVehicleTimetable(v, true);
1454 v->IncrementImplicitOrderIndex();
1455 }
1456 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1457 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1458 _vehicles_to_autoreplace[v] = false;
1459 /* Invalidate last_loading_station. As the link from the station
1460 * before the stop to the station after the stop can't be predicted
1461 * we shouldn't construct it when the vehicle visits the next stop. */
1462 v->last_loading_station = INVALID_STATION;
1463 if (v->owner == _local_company) {
1464 SetDParam(0, v->index);
1465 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1466 }
1467 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1468 }
1469 v->current_order.MakeDummy();
1470 }
1471 }
1472
1473
1478 void Vehicle::UpdatePosition()
1479 {
1480 UpdateVehicleTileHash(this, false);
1481 }
1482
1488 void Vehicle::UpdateViewport(bool dirty)
1489 {
1490 int img = this->cur_image;
1491 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1492 const Sprite *spr = GetSprite(img, ST_NORMAL);
1493
1494 pt.x += spr->x_offs;
1495 pt.y += spr->y_offs;
1496
1497 UpdateVehicleViewportHash(this, pt.x, pt.y);
1498
1499 Rect old_coord = this->coord;
1500 this->coord.left = pt.x;
1501 this->coord.top = pt.y;
1502 this->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
1503 this->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
1504
1505 if (dirty) {
1506 if (old_coord.left == INVALID_COORD) {
1507 this->MarkAllViewportsDirty();
1508 } else {
1509 ::MarkAllViewportsDirty(
1510 min(old_coord.left, this->coord.left),
1511 min(old_coord.top, this->coord.top),
1512 max(old_coord.right, this->coord.right),
1513 max(old_coord.bottom, this->coord.bottom));
1514 }
1515 }
1516 }
1517
1521 void Vehicle::UpdatePositionAndViewport()
1522 {
1523 this->UpdatePosition();
1524 this->UpdateViewport(true);
1525 }
1526
1530 void Vehicle::MarkAllViewportsDirty() const
1531 {
1532 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1533 }
1534
1540 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1541 {
1542 static const int8 _delta_coord[16] = {
1543 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1544 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1545 };
1546
1547 int x = v->x_pos + _delta_coord[v->direction];
1548 int y = v->y_pos + _delta_coord[v->direction + 8];
1549
1550 GetNewVehiclePosResult gp;
1551 gp.x = x;
1552 gp.y = y;
1553 gp.old_tile = v->tile;
1554 gp.new_tile = TileVirtXY(x, y);
1555 return gp;
1556 }
1557
1558 static const Direction _new_direction_table[] = {
1559 DIR_N, DIR_NW, DIR_W,
1560 DIR_NE, DIR_SE, DIR_SW,
1561 DIR_E, DIR_SE, DIR_S
1562 };
1563
1564 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1565 {
1566 int i = 0;
1567
1568 if (y >= v->y_pos) {
1569 if (y != v->y_pos) i += 3;
1570 i += 3;
1571 }
1572
1573 if (x >= v->x_pos) {
1574 if (x != v->x_pos) i++;
1575 i++;
1576 }
1577
1578 Direction dir = v->direction;
1579
1580 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1581 if (dirdiff == DIRDIFF_SAME) return dir;
1582 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1583 }
1584
1594 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1595 {
1596 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1597 }
1598
1606 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1607 {
1608 /* Find maximum */
1609 const Vehicle *v;
1610 FOR_ALL_VEHICLES(v) {
1611 if (v->type == type && v->owner == owner) {
1612 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1613 }
1614 }
1615
1616 if (this->maxid == 0) return;
1617
1618 /* Reserving 'maxid + 2' because we need:
1619 * - space for the last item (with v->unitnumber == maxid)
1620 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1621 this->cache = CallocT<bool>(this->maxid + 2);
1622
1623 /* Fill the cache */
1624 FOR_ALL_VEHICLES(v) {
1625 if (v->type == type && v->owner == owner) {
1626 this->cache[v->unitnumber] = true;
1627 }
1628 }
1629 }
1630
1632 UnitID FreeUnitIDGenerator::NextID()
1633 {
1634 if (this->maxid <= this->curid) return ++this->curid;
1635
1636 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1637
1638 return this->curid;
1639 }
1640
1646 UnitID GetFreeUnitNumber(VehicleType type)
1647 {
1648 /* Check whether it is allowed to build another vehicle. */
1649 uint max_veh;
1650 switch (type) {
1651 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1652 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1653 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1654 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1655 default: NOT_REACHED();
1656 }
1657
1658 const Company *c = Company::Get(_current_company);
1659 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1660
1661 FreeUnitIDGenerator gen(type, _current_company);
1662
1663 return gen.NextID();
1664 }
1665
1666
1675 bool CanBuildVehicleInfrastructure(VehicleType type)
1676 {
1677 assert(IsCompanyBuildableVehicleType(type));
1678
1679 if (!Company::IsValidID(_local_company)) return false;
1680 if (!_settings_client.gui.disable_unsuitable_building) return true;
1681
1682 UnitID max;
1683 switch (type) {
1684 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1685 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1686 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1687 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1688 default: NOT_REACHED();
1689 }
1690
1691 /* We can build vehicle infrastructure when we may build the vehicle type */
1692 if (max > 0) {
1693 /* Can we actually build the vehicle type? */
1694 const Engine *e;
1695 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1696 if (HasBit(e->company_avail, _local_company)) return true;
1697 }
1698 return false;
1699 }
1700
1701 /* We should be able to build infrastructure when we have the actual vehicle type */
1702 const Vehicle *v;
1703 FOR_ALL_VEHICLES(v) {
1704 if (v->owner == _local_company && v->type == type) return true;
1705 }
1706
1707 return false;
1708 }
1709
1710
1718 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1719 {
1720 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1721 const Engine *e = Engine::Get(engine_type);
1722 switch (e->type) {
1723 default: NOT_REACHED();
1724 case VEH_TRAIN:
1725 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1726 /* Wagonoverrides use the colour scheme of the front engine.
1727 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1728 engine_type = parent_engine_type;
1729 e = Engine::Get(engine_type);
1730 /* Note: Luckily cargo_type is not needed for engines */
1731 }
1732
1733 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1734 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1735 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1736 if (!CargoSpec::Get(cargo_type)->is_freight) {
1737 if (parent_engine_type == INVALID_ENGINE) {
1738 return LS_PASSENGER_WAGON_STEAM;
1739 } else {
1740 switch (RailVehInfo(parent_engine_type)->engclass) {
1741 default: NOT_REACHED();
1742 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1743 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1744 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1745 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1746 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1747 }
1748 }
1749 } else {
1750 return LS_FREIGHT_WAGON;
1751 }
1752 } else {
1753 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1754
1755 switch (e->u.rail.engclass) {
1756 default: NOT_REACHED();
1757 case EC_STEAM: return LS_STEAM;
1758 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1759 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1760 case EC_MONORAIL: return LS_MONORAIL;
1761 case EC_MAGLEV: return LS_MAGLEV;
1762 }
1763 }
1764
1765 case VEH_ROAD:
1766 /* Always use the livery of the front */
1767 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1768 engine_type = parent_engine_type;
1769 e = Engine::Get(engine_type);
1770 cargo_type = v->First()->cargo_type;
1771 }
1772 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1773 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1774
1775 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1776 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1777 /* Tram */
1778 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1779 } else {
1780 /* Bus or truck */
1781 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1782 }
1783
1784 case VEH_SHIP:
1785 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1786 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1787 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1788
1789 case VEH_AIRCRAFT:
1790 switch (e->u.air.subtype) {
1791 case AIR_HELI: return LS_HELICOPTER;
1792 case AIR_CTOL: return LS_SMALL_PLANE;
1793 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1794 default: NOT_REACHED();
1795 }
1796 }
1797 }
1798
1808 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1809 {
1810 const Company *c = Company::Get(company);
1811 LiveryScheme scheme = LS_DEFAULT;
1812
1813 /* The default livery is always available for use, but its in_use flag determines
1814 * whether any _other_ liveries are in use. */
1815 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1816 /* Determine the livery scheme to use */
1817 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1818
1819 /* Switch back to the default scheme if the resolved scheme is not in use */
1820 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1821 }
1822
1823 return &c->livery[scheme];
1824 }
1825
1826
1827 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1828 {
1829 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1830
1831 /* Return cached value if any */
1832 if (map != PAL_NONE) return map;
1833
1834 const Engine *e = Engine::Get(engine_type);
1835
1836 /* Check if we should use the colour map callback */
1837 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1838 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1839 /* Failure means "use the default two-colour" */
1840 if (callback != CALLBACK_FAILED) {
1841 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1842 map = GB(callback, 0, 14);
1843 /* If bit 14 is set, then the company colours are applied to the
1844 * map else it's returned as-is. */
1845 if (!HasBit(callback, 14)) {
1846 /* Update cache */
1847 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1848 return map;
1849 }
1850 }
1851 }
1852
1853 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1854
1855 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1856
1857 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1858 if (!Company::IsValidID(company)) return map;
1859
1860 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1861
1862 map += livery->colour1;
1863 if (twocc) map += livery->colour2 * 16;
1864
1865 /* Update cache */
1866 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1867 return map;
1868 }
1869
1876 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1877 {
1878 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1879 }
1880
1886 PaletteID GetVehiclePalette(const Vehicle *v)
1887 {
1888 if (v->IsGroundVehicle()) {
1889 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1890 }
1891
1892 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1893 }
1894
1898 void Vehicle::DeleteUnreachedImplicitOrders()
1899 {
1900 if (this->IsGroundVehicle()) {
1901 uint16 &gv_flags = this->GetGroundVehicleFlags();
1902 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1903 /* Do not delete orders, only skip them */
1904 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1905 this->cur_implicit_order_index = this->cur_real_order_index;
1906 InvalidateVehicleOrder(this, 0);
1907 return;
1908 }
1909 }
1910
1911 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1912 while (order != NULL) {
1913 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1914
1915 if (order->IsType(OT_IMPLICIT)) {
1916 DeleteOrder(this, this->cur_implicit_order_index);
1917 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1918 order = this->GetOrder(this->cur_implicit_order_index);
1919 } else {
1920 /* Skip non-implicit orders, e.g. service-orders */
1921 order = order->next;
1922 this->cur_implicit_order_index++;
1923 }
1924
1925 /* Wrap around */
1926 if (order == NULL) {
1927 order = this->GetOrder(0);
1928 this->cur_implicit_order_index = 0;
1929 }
1930 }
1931 }
1932
1937 void Vehicle::BeginLoading()
1938 {
1939 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
1940
1941 if (this->current_order.IsType(OT_GOTO_STATION) &&
1942 this->current_order.GetDestination() == this->last_station_visited) {
1943 this->DeleteUnreachedImplicitOrders();
1944
1945 /* Now both order indices point to the destination station, and we can start loading */
1946 this->current_order.MakeLoading(true);
1947 UpdateVehicleTimetable(this, true);
1948
1949 /* Furthermore add the Non Stop flag to mark that this station
1950 * is the actual destination of the vehicle, which is (for example)
1951 * necessary to be known for HandleTrainLoading to determine
1952 * whether the train is lost or not; not marking a train lost
1953 * that arrives at random stations is bad. */
1954 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1955
1956 } else {
1957 /* We weren't scheduled to stop here. Insert an implicit order
1958 * to show that we are stopping here.
1959 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
1960 * the 'wrong' terminal when skipping orders etc. */
1961 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
1962 if (this->IsGroundVehicle() &&
1963 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
1964 in_list->GetDestination() != this->last_station_visited)) {
1965 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
1966 /* Do not create consecutive duplicates of implicit orders */
1967 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
1968 if (prev_order == NULL ||
1969 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
1970 prev_order->GetDestination() != this->last_station_visited) {
1971
1972 /* Prefer deleting implicit orders instead of inserting new ones,
1973 * so test whether the right order follows later. In case of only
1974 * implicit orders treat the last order in the list like an
1975 * explicit one, except if the overall number of orders surpasses
1976 * IMPLICIT_ORDER_ONLY_CAP. */
1977 int target_index = this->cur_implicit_order_index;
1978 bool found = false;
1979 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
1980 const Order *order = this->GetOrder(target_index);
1981 if (order == NULL) break; // No orders.
1982 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
1983 found = true;
1984 break;
1985 }
1986 target_index++;
1987 if (target_index >= this->orders.list->GetNumOrders()) {
1988 if (this->GetNumManualOrders() == 0 &&
1989 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
1990 break;
1991 }
1992 target_index = 0;
1993 }
1994 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
1995 }
1996
1997 if (found) {
1998 if (suppress_implicit_orders) {
1999 /* Skip to the found order */
2000 this->cur_implicit_order_index = target_index;
2001 InvalidateVehicleOrder(this, 0);
2002 } else {
2003 /* Delete all implicit orders up to the station we just reached */
2004 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2005 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2006 if (order->IsType(OT_IMPLICIT)) {
2007 DeleteOrder(this, this->cur_implicit_order_index);
2008 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2009 order = this->GetOrder(this->cur_implicit_order_index);
2010 } else {
2011 /* Skip non-implicit orders, e.g. service-orders */
2012 order = order->next;
2013 this->cur_implicit_order_index++;
2014 }
2015
2016 /* Wrap around */
2017 if (order == NULL) {
2018 order = this->GetOrder(0);
2019 this->cur_implicit_order_index = 0;
2020 }
2021 assert(order != NULL);
2022 }
2023 }
2024 } else if (!suppress_implicit_orders &&
2025 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2026 Order::CanAllocateItem()) {
2027 /* Insert new implicit order */
2028 Order *implicit_order = new Order();
2029 implicit_order->MakeImplicit(this->last_station_visited);
2030 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2031 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2032
2033 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2034 * Reenable it for this vehicle */
2035 uint16 &gv_flags = this->GetGroundVehicleFlags();
2036 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2037 }
2038 }
2039 }
2040 this->current_order.MakeLoading(false);
2041 }
2042
2043 if (this->last_loading_station != INVALID_STATION &&
2044 this->last_loading_station != this->last_station_visited &&
2045 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2046 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2047 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2048 }
2049
2050 PrepareUnload(this);
2051
2052 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2053 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2054 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2055 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2056
2057 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2058 this->cur_speed = 0;
2059 this->MarkDirty();
2060 }
2061
2067 void Vehicle::CancelReservation(StationID next, Station *st)
2068 {
2069 for (Vehicle *v = this; v != NULL; v = v->next) {
2070 VehicleCargoList &cargo = v->cargo;
2071 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2072 DEBUG(misc, 1, "cancelling cargo reservation");
2073 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2074 cargo.SetTransferLoadPlace(st->xy);
2075 }
2076 cargo.KeepAll();
2077 }
2078 }
2079
2084 void Vehicle::LeaveStation()
2085 {
2086 assert(this->current_order.IsType(OT_LOADING));
2087
2088 delete this->cargo_payment;
2089 assert(this->cargo_payment == NULL); // cleared by ~CargoPayment
2090
2091 /* Only update the timetable if the vehicle was supposed to stop here. */
2092 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2093
2094 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2095 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2096 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2097 /* Refresh next hop stats to make sure we've done that at least once
2098 * during the stop and that refit_cap == cargo_cap for each vehicle in
2099 * the consist. */
2100 this->ResetRefitCaps();
2101 LinkRefresher::Run(this);
2102
2103 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2104 this->last_loading_station = this->last_station_visited;
2105 } else {
2106 /* if the vehicle couldn't load and had to unload or transfer everything
2107 * set the last loading station to invalid as it will leave empty. */
2108 this->last_loading_station = INVALID_STATION;
2109 }
2110 }
2111
2112 this->current_order.MakeLeaveStation();
2113 Station *st = Station::Get(this->last_station_visited);
2114 this->CancelReservation(INVALID_STATION, st);
2115 st->loading_vehicles.remove(this);
2116
2117 HideFillingPercent(&this->fill_percent_te_id);
2118
2119 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2120 /* Trigger station animation (trains only) */
2121 if (IsTileType(this->tile, MP_STATION)) {
2122 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2123 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2124 }
2125
2126 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2127 }
2128
2129 this->MarkDirty();
2130 }
2131
2135 void Vehicle::ResetRefitCaps()
2136 {
2137 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2138 }
2139
2145 void Vehicle::HandleLoading(bool mode)
2146 {
2147 switch (this->current_order.GetType()) {
2148 case OT_LOADING: {
2149 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2150
2151 /* Not the first call for this tick, or still loading */
2152 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2153
2154 this->PlayLeaveStationSound();
2155
2156 this->LeaveStation();
2157
2158 /* Only advance to next order if we just loaded at the current one */
2159 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2160 if (order == NULL ||
2161 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2162 order->GetDestination() != this->last_station_visited) {
2163 return;
2164 }
2165 break;
2166 }
2167
2168 case OT_DUMMY: break;
2169
2170 default: return;
2171 }
2172
2173 this->IncrementImplicitOrderIndex();
2174 }
2175
2180 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2181 {
2182 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2183 if (v->cargo_cap == 0) continue;
2184 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2185 if (pair == capacities.End()) {
2186 pair = capacities.Append();
2187 pair->first = v->cargo_type;
2188 pair->second = v->cargo_cap - v->cargo.StoredCount();
2189 } else {
2190 pair->second += v->cargo_cap - v->cargo.StoredCount();
2191 }
2192 }
2193 }
2194
2195 uint Vehicle::GetConsistTotalCapacity() const
2196 {
2197 uint result = 0;
2198 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2199 result += v->cargo_cap;
2200 }
2201 return result;
2202 }
2203
2210 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2211 {
2212 CommandCost ret = CheckOwnership(this->owner);
2213 if (ret.Failed()) return ret;
2214
2215 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2216 if (this->IsStoppedInDepot()) return CMD_ERROR;
2217
2218 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2219 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2220 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2221 /* We called with a different DEPOT_SERVICE setting.
2222 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2223 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2224 if (flags & DC_EXEC) {
2225 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2226 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2227 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2228 }
2229 return CommandCost();
2230 }
2231
2232 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2233 if (flags & DC_EXEC) {
2234 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2235 * then skip to the next order; effectively cancelling this forced service */
2236 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2237
2238 if (this->IsGroundVehicle()) {
2239 uint16 &gv_flags = this->GetGroundVehicleFlags();
2240 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2241 }
2242
2243 this->current_order.MakeDummy();
2244 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2245 }
2246 return CommandCost();
2247 }
2248
2249 TileIndex location;
2250 DestinationID destination;
2251 bool reverse;
2252 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};
2253 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2254
2255 if (flags & DC_EXEC) {
2256 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2257
2258 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2259 uint16 &gv_flags = this->GetGroundVehicleFlags();
2260 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2261 }
2262
2263 this->dest_tile = location;
2264 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2265 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2266 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2267
2268 /* If there is no depot in front, reverse automatically (trains only) */
2269 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2270
2271 if (this->type == VEH_AIRCRAFT) {
2272 Aircraft *a = Aircraft::From(this);
2273 if (a->state == FLYING && a->targetairport != destination) {
2274 /* The aircraft is now heading for a different hangar than the next in the orders */
2275 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2276 AircraftNextAirportPos_and_Order(a);
2277 }
2278 }
2279 }
2280
2281 return CommandCost();
2282
2283 }
2284
2289 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2290 {
2291 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2292 const Engine *e = this->GetEngine();
2293
2294 /* Evaluate properties */
2295 byte visual_effect;
2296 switch (e->type) {
2297 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2298 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2299 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2300 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2301 }
2302
2303 /* Check powered wagon / visual effect callback */
2304 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2305 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2306
2307 if (callback != CALLBACK_FAILED) {
2308 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2309
2310 callback = GB(callback, 0, 8);
2311 /* Avoid accidentally setting 'visual_effect' to the default value
2312 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2313 if (callback == VE_DEFAULT) {
2314 assert(HasBit(callback, VE_DISABLE_EFFECT));
2315 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2316 }
2317 visual_effect = callback;
2318 }
2319 }
2320
2321 /* Apply default values */
2322 if (visual_effect == VE_DEFAULT ||
2323 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2324 /* Only train engines have default effects.
2325 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2326 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2327 if (visual_effect == VE_DEFAULT) {
2328 visual_effect = 1 << VE_DISABLE_EFFECT;
2329 } else {
2330 SetBit(visual_effect, VE_DISABLE_EFFECT);
2331 }
2332 } else {
2333 if (visual_effect == VE_DEFAULT) {
2334 /* Also set the offset */
2335 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2336 }
2337 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2338 }
2339 }
2340
2341 this->vcache.cached_vis_effect = visual_effect;
2342
2343 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2344 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2345 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2346 }
2347 }
2348
2349 static const int8 _vehicle_smoke_pos[8] = {
2350 1, 1, 1, 0, -1, -1, -1, 0
2351 };
2352
2357 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2358 {
2359 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2360 if (callback == CALLBACK_FAILED) return;
2361
2362 uint count = GB(callback, 0, 2);
2363 bool auto_center = HasBit(callback, 13);
2364 bool auto_rotate = !HasBit(callback, 14);
2365
2366 int8 l_center = 0;
2367 if (auto_center) {
2368 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2369 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2370 } else {
2371 /* For trains: Compute offset from vehicle position to sprite position */
2372 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2373 }
2374
2375 Direction l_dir = v->direction;
2376 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2377 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2378
2379 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2380 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2381
2382 for (uint i = 0; i < count; i++) {
2383 uint32 reg = GetRegister(0x100 + i);
2384 uint type = GB(reg, 0, 8);
2385 int8 x = GB(reg, 8, 8);
2386 int8 y = GB(reg, 16, 8);
2387 int8 z = GB(reg, 24, 8);
2388
2389 if (auto_rotate) {
2390 int8 l = x;
2391 int8 t = y;
2392 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2393 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2394 }
2395
2396 if (type >= 0xF0) {
2397 switch (type) {
2398 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2399 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2400 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2401 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2402 default: break;
2403 }
2404 }
2405 }
2406 }
2407
2412 void Vehicle::ShowVisualEffect() const
2413 {
2414 assert(this->IsPrimaryVehicle());
2415 bool sound = false;
2416
2417 /* Do not show any smoke when:
2418 * - vehicle smoke is disabled by the player
2419 * - the vehicle is slowing down or stopped (by the player)
2420 * - the vehicle is moving very slowly
2421 */
2422 if (_settings_game.vehicle.smoke_amount == 0 ||
2423 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2424 this->cur_speed < 2) {
2425 return;
2426 }
2427
2428 /* Use the speed as limited by underground and orders. */
2429 uint max_speed = this->GetCurrentMaxSpeed();
2430
2431 if (this->type == VEH_TRAIN) {
2432 const Train *t = Train::From(this);
2433 /* For trains, do not show any smoke when:
2434 * - the train is reversing
2435 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2436 */
2437 if (HasBit(t->flags, VRF_REVERSING) ||
2438 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2439 t->cur_speed >= max_speed)) {
2440 return;
2441 }
2442 }
2443
2444 const Vehicle *v = this;
2445
2446 do {
2447 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2448 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2449 VisualEffectSpawnModel effect_model = VESM_NONE;
2450 if (advanced) {
2451 effect_offset = VE_OFFSET_CENTRE;
2452 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2453 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2454 } else {
2455 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2456 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2457 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2458 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2459 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2460 }
2461
2462 /* Show no smoke when:
2463 * - Smoke has been disabled for this vehicle
2464 * - The vehicle is not visible
2465 * - The vehicle is under a bridge
2466 * - The vehicle is on a depot tile
2467 * - The vehicle is on a tunnel tile
2468 * - The vehicle is a train engine that is currently unpowered */
2469 if (effect_model == VESM_NONE ||
2470 v->vehstatus & VS_HIDDEN ||
2471 IsBridgeAbove(v->tile) ||
2472 IsDepotTile(v->tile) ||
2473 IsTunnelTile(v->tile) ||
2474 (v->type == VEH_TRAIN &&
2475 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2476 continue;
2477 }
2478
2479 EffectVehicleType evt = EV_END;
2480 switch (effect_model) {
2481 case VESM_STEAM:
2482 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2483 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2484 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2485 * REGULATION:
2486 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2487 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2488 evt = EV_STEAM_SMOKE;
2489 }
2490 break;
2491
2492 case VESM_DIESEL: {
2493 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2494 * when smoke emission stops.
2495 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2496 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2497 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2498 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2499 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2500 * maximum speed no diesel_smoke is emitted.
2501 * REGULATION:
2502 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2503 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2504 int power_weight_effect = 0;
2505 if (v->type == VEH_TRAIN) {
2506 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2507 }
2508 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2509 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2510 evt = EV_DIESEL_SMOKE;
2511 }
2512 break;
2513 }
2514
2515 case VESM_ELECTRIC:
2516 /* Electric train's spark - more often occurs when train is departing (more load)
2517 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2518 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2519 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2520 * REGULATION:
2521 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2522 if (GB(v->tick_counter, 0, 2) == 0 &&
2523 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2524 evt = EV_ELECTRIC_SPARK;
2525 }
2526 break;
2527
2528 default:
2529 NOT_REACHED();
2530 }
2531
2532 if (evt != EV_END && advanced) {
2533 sound = true;
2534 SpawnAdvancedVisualEffect(v);
2535 } else if (evt != EV_END) {
2536 sound = true;
2537
2538 /* The effect offset is relative to a point 4 units behind the vehicle's
2539 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2540 * correction factor. */
2541 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2542
2543 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2544 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2545
2546 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2547 x = -x;
2548 y = -y;
2549 }
2550
2551 CreateEffectVehicleRel(v, x, y, 10, evt);
2552 }
2553 } while ((v = v->Next()) != NULL);
2554
2555 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2556 }
2557
2562 void Vehicle::SetNext(Vehicle *next)
2563 {
2564 assert(this != next);
2565
2566 if (this->next != NULL) {
2567 /* We had an old next vehicle. Update the first and previous pointers */
2568 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2569 v->first = this->next;
2570 }
2571 this->next->previous = NULL;
2572 }
2573
2574 this->next = next;
2575
2576 if (this->next != NULL) {
2577 /* A new next vehicle. Update the first and previous pointers */
2578 if (this->next->previous != NULL) this->next->previous->next = NULL;
2579 this->next->previous = this;
2580 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2581 v->first = this->first;
2582 }
2583 }
2584 }
2585
2591 void Vehicle::AddToShared(Vehicle *shared_chain)
2592 {
2593 assert(this->previous_shared == NULL && this->next_shared == NULL);
2594
2595 if (shared_chain->orders.list == NULL) {
2596 assert(shared_chain->previous_shared == NULL);
2597 assert(shared_chain->next_shared == NULL);
2598 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2599 }
2600
2601 this->next_shared = shared_chain->next_shared;
2602 this->previous_shared = shared_chain;
2603
2604 shared_chain->next_shared = this;
2605
2606 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2607
2608 shared_chain->orders.list->AddVehicle(this);
2609 }
2610
2614 void Vehicle::RemoveFromShared()
2615 {
2616 /* Remember if we were first and the old window number before RemoveVehicle()
2617 * as this changes first if needed. */
2618 bool were_first = (this->FirstShared() == this);
2619 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2620
2621 this->orders.list->RemoveVehicle(this);
2622
2623 if (!were_first) {
2624 /* We are not the first shared one, so only relink our previous one. */
2625 this->previous_shared->next_shared = this->NextShared();
2626 }
2627
2628 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2629
2630
2631 if (this->orders.list->GetNumVehicles() == 1) {
2632 /* When there is only one vehicle, remove the shared order list window. */
2633 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2634 InvalidateVehicleOrder(this->FirstShared(), 0);
2635 } else if (were_first) {
2636 /* If we were the first one, update to the new first one.
2637 * Note: FirstShared() is already the new first */
2638 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2639 }
2640
2641 this->next_shared = NULL;
2642 this->previous_shared = NULL;
2643 }
2644
2645 void VehiclesYearlyLoop()
2646 {
2647 Vehicle *v;
2648 FOR_ALL_VEHICLES(v) {
2649 if (v->IsPrimaryVehicle()) {
2650 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2651 Money profit = v->GetDisplayProfitThisYear();
2652 if (v->age >= 730 && profit < 0) {
2653 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2654 SetDParam(0, v->index);
2655 SetDParam(1, profit);
2656 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2657 }
2658 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2659 }
2660
2661 v->profit_last_year = v->profit_this_year;
2662 v->profit_this_year = 0;
2663 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2664 }
2665 }
2666 GroupStatistics::UpdateProfits();
2667 SetWindowClassesDirty(WC_TRAINS_LIST);
2668 SetWindowClassesDirty(WC_SHIPS_LIST);
2669 SetWindowClassesDirty(WC_ROADVEH_LIST);
2670 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2671 }
2672
2673
2683 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2684 {
2685 const Engine *e = Engine::GetIfValid(engine_type);
2686 assert(e != NULL);
2687
2688 switch (e->type) {
2689 case VEH_TRAIN:
2690 return (st->facilities & FACIL_TRAIN) != 0;
2691
2692 case VEH_ROAD:
2693 /* For road vehicles we need the vehicle to know whether it can actually
2694 * use the station, but if it doesn't have facilities for RVs it is
2695 * certainly not possible that the station can be used. */
2696 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2697
2698 case VEH_SHIP:
2699 return (st->facilities & FACIL_DOCK) != 0;
2700
2701 case VEH_AIRCRAFT:
2702 return (st->facilities & FACIL_AIRPORT) != 0 &&
2703 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2704
2705 default:
2706 return false;
2707 }
2708 }
2709
2716 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2717 {
2718 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2719
2720 return CanVehicleUseStation(v->engine_type, st);
2721 }
2722
2728 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2729 {
2730 assert(this->IsGroundVehicle());
2731 if (this->type == VEH_TRAIN) {
2732 return &Train::From(this)->gcache;
2733 } else {
2734 return &RoadVehicle::From(this)->gcache;
2735 }
2736 }
2737
2743 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2744 {
2745 assert(this->IsGroundVehicle());
2746 if (this->type == VEH_TRAIN) {
2747 return &Train::From(this)->gcache;
2748 } else {
2749 return &RoadVehicle::From(this)->gcache;
2750 }
2751 }
2752
2758 uint16 &Vehicle::GetGroundVehicleFlags()
2759 {
2760 assert(this->IsGroundVehicle());
2761 if (this->type == VEH_TRAIN) {
2762 return Train::From(this)->gv_flags;
2763 } else {
2764 return RoadVehicle::From(this)->gv_flags;
2765 }
2766 }
2767
2773 const uint16 &Vehicle::GetGroundVehicleFlags() const
2774 {
2775 assert(this->IsGroundVehicle());
2776 if (this->type == VEH_TRAIN) {
2777 return Train::From(this)->gv_flags;
2778 } else {
2779 return RoadVehicle::From(this)->gv_flags;
2780 }
2781 }
2782
2791 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2792 {
2793 if (v->type == VEH_TRAIN) {
2794 Train *u = Train::From(v);
2795 /* Only include whole vehicles, so start with the first articulated part */
2796 u = u->GetFirstEnginePart();
2797
2798 /* Include num_vehicles vehicles, not counting articulated parts */
2799 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2800 do {
2801 /* Include current vehicle in the selection. */
2802 set.Include(u->index);
2803
2804 /* If the vehicle is multiheaded, add the other part too. */
2805 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2806
2807 u = u->Next();
2808 } while (u != NULL && u->IsArticulatedPart());
2809 }
2810 }
2811 }