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 27426 2015-10-30 17:24:05Z 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 this->cargo_payment = NULL;
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 }
750
751 if (this->IsEngineCountable()) {
752 GroupStatistics::CountEngine(this, -1);
753 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
754 GroupStatistics::UpdateAutoreplace(this->owner);
755
756 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
757 DeleteGroupHighlightOfVehicle(this);
758 }
759
760 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
761 Aircraft *a = Aircraft::From(this);
762 Station *st = GetTargetAirportIfValid(a);
763 if (st != NULL) {
764 const AirportFTA *layout = st->airport.GetFTA()->layout;
765 CLRBITS(st->airport.flags, layout[a->previous_pos].block | layout[a->pos].block);
766 }
767 }
768
769
770 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
771 RoadVehicle *v = RoadVehicle::From(this);
772 if (!(v->vehstatus & VS_CRASHED) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
773 /* Leave the drive through roadstop, when you have not already left it. */
774 RoadStop::GetByTile(v->tile, GetRoadStopType(v->tile))->Leave(v);
775 }
776 }
777
778 if (this->Previous() == NULL) {
779 InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile);
780 }
781
782 if (this->IsPrimaryVehicle()) {
783 DeleteWindowById(WC_VEHICLE_VIEW, this->index);
784 DeleteWindowById(WC_VEHICLE_ORDERS, this->index);
785 DeleteWindowById(WC_VEHICLE_REFIT, this->index);
786 DeleteWindowById(WC_VEHICLE_DETAILS, this->index);
787 DeleteWindowById(WC_VEHICLE_TIMETABLE, this->index);
788 SetWindowDirty(WC_COMPANY, this->owner);
789 OrderBackup::ClearVehicle(this);
790 }
791 InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type), 0);
792
793 this->cargo.Truncate();
794 DeleteVehicleOrders(this);
795 DeleteDepotHighlightOfVehicle(this);
796
797 extern void StopGlobalFollowVehicle(const Vehicle *v);
798 StopGlobalFollowVehicle(this);
799
800 ReleaseDisastersTargetingVehicle(this->index);
801 }
802
803 Vehicle::~Vehicle()
804 {
805 if (CleaningPool()) {
806 this->cargo.OnCleanPool();
807 return;
808 }
809
810 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
811 * it may happen that vehicle chain is deleted when visible */
812 if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty();
813
814 Vehicle *v = this->Next();
815 this->SetNext(NULL);
816
817 delete v;
818
819 UpdateVehicleTileHash(this, true);
820 UpdateVehicleViewportHash(this, INVALID_COORD, 0);
821 DeleteVehicleNews(this->index, INVALID_STRING_ID);
822 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
823 }
824
829 void VehicleEnteredDepotThisTick(Vehicle *v)
830 {
831 /* Vehicle should stop in the depot if it was in 'stopping' state */
832 _vehicles_to_autoreplace[v] = !(v->vehstatus & VS_STOPPED);
833
834 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
835 * stopping in the depot, so we stop it to ensure that it will not reserve
836 * the path out of the depot before we might autoreplace it to a different
837 * engine. The new engine would not own the reserved path we store that we
838 * stopped the vehicle, so autoreplace can start it again */
839 v->vehstatus |= VS_STOPPED;
840 }
841
847 static void RunVehicleDayProc()
848 {
849 if (_game_mode != GM_NORMAL) return;
850
851 /* Run the day_proc for every DAY_TICKS vehicle starting at _date_fract. */
852 for (size_t i = _date_fract; i < Vehicle::GetPoolSize(); i += DAY_TICKS) {
853 Vehicle *v = Vehicle::Get(i);
854 if (v == NULL) continue;
855
856 /* Call the 32-day callback if needed */
857 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
858 uint16 callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
859 if (callback != CALLBACK_FAILED) {
860 if (HasBit(callback, 0)) {
861 TriggerVehicle(v, VEHICLE_TRIGGER_CALLBACK_32); // Trigger vehicle trigger 10
862 }
863
864 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
865 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
866 if (callback != 0) v->First()->MarkDirty();
867
868 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
869 }
870 }
871
872 /* This is called once per day for each vehicle, but not in the first tick of the day */
873 v->OnNewDay();
874 }
875 }
876
877 void CallVehicleTicks()
878 {
879 _vehicles_to_autoreplace.Clear();
880
881 RunVehicleDayProc();
882
883 Station *st;
884 FOR_ALL_STATIONS(st) LoadUnloadStation(st);
885
886 Vehicle *v;
887 FOR_ALL_VEHICLES(v) {
888 /* Vehicle could be deleted in this tick */
889 if (!v->Tick()) {
890 assert(Vehicle::Get(vehicle_index) == NULL);
891 continue;
892 }
893
894 assert(Vehicle::Get(vehicle_index) == v);
895
896 switch (v->type) {
897 default: break;
898
899 case VEH_TRAIN:
900 case VEH_ROAD:
901 case VEH_AIRCRAFT:
902 case VEH_SHIP: {
903 Vehicle *front = v->First();
904
905 if (v->vcache.cached_cargo_age_period != 0) {
906 v->cargo_age_counter = min(v->cargo_age_counter, v->vcache.cached_cargo_age_period);
907 if (--v->cargo_age_counter == 0) {
908 v->cargo.AgeCargo();
909 v->cargo_age_counter = v->vcache.cached_cargo_age_period;
910 }
911 }
912
913 /* Do not play any sound when crashed */
914 if (front->vehstatus & VS_CRASHED) continue;
915
916 /* Do not play any sound when in depot or tunnel */
917 if (v->vehstatus & VS_HIDDEN) continue;
918
919 /* Do not play any sound when stopped */
920 if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
921
922 /* Check vehicle type specifics */
923 switch (v->type) {
924 case VEH_TRAIN:
925 if (Train::From(v)->IsWagon()) continue;
926 break;
927
928 case VEH_ROAD:
929 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
930 break;
931
932 case VEH_AIRCRAFT:
933 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
934 break;
935
936 default:
937 break;
938 }
939
940 v->motion_counter += front->cur_speed;
941 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
942 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
943
944 /* Play an alternating running sound every 16 ticks */
945 if (GB(v->tick_counter, 0, 4) == 0) {
946 /* Play running sound when speed > 0 and not braking */
947 bool running = (front->cur_speed > 0) && !(front->vehstatus & (VS_STOPPED | VS_TRAIN_SLOWING));
948 PlayVehicleSound(v, running ? VSE_RUNNING_16 : VSE_STOPPED_16);
949 }
950
951 break;
952 }
953 }
954 }
955
956 Backup<CompanyByte> cur_company(_current_company, FILE_LINE);
957 for (AutoreplaceMap::iterator it = _vehicles_to_autoreplace.Begin(); it != _vehicles_to_autoreplace.End(); it++) {
958 v = it->first;
959 /* Autoreplace needs the current company set as the vehicle owner */
960 cur_company.Change(v->owner);
961
962 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
963 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
964 * they are already leaving the depot again before being replaced. */
965 if (it->second) v->vehstatus &= ~VS_STOPPED;
966
967 /* Store the position of the effect as the vehicle pointer will become invalid later */
968 int x = v->x_pos;
969 int y = v->y_pos;
970 int z = v->z_pos;
971
972 const Company *c = Company::Get(_current_company);
973 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, (Money)c->settings.engine_renew_money));
974 CommandCost res = DoCommand(0, v->index, 0, DC_EXEC, CMD_AUTOREPLACE_VEHICLE);
975 SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -(Money)c->settings.engine_renew_money));
976
977 if (!IsLocalCompany()) continue;
978
979 if (res.Succeeded()) {
980 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
981 continue;
982 }
983
984 StringID error_message = res.GetErrorMessage();
985 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
986
987 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
988
989 StringID message;
990 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
991 message = error_message;
992 } else {
993 message = STR_NEWS_VEHICLE_AUTORENEW_FAILED;
994 }
995
996 SetDParam(0, v->index);
997 SetDParam(1, error_message);
998 AddVehicleAdviceNewsItem(message, v->index);
999 }
1000
1001 cur_company.Restore();
1002 }
1003
1008 static void DoDrawVehicle(const Vehicle *v)
1009 {
1010 SpriteID image = v->cur_image;
1011 PaletteID pal = PAL_NONE;
1012
1013 if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v);
1014
1015 /* Check whether the vehicle shall be transparent due to the game state */
1016 bool shadowed = (v->vehstatus & VS_SHADOW) != 0;
1017
1018 if (v->type == VEH_EFFECT) {
1019 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1020 * However, transparent smoke and bubbles look weird, so always hide them. */
1021 TransparencyOption to = EffectVehicle::From(v)->GetTransparencyOption();
1022 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1023 }
1024
1025 AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1026 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1027 }
1028
1033 void ViewportAddVehicles(DrawPixelInfo *dpi)
1034 {
1035 /* The bounding rectangle */
1036 const int l = dpi->left;
1037 const int r = dpi->left + dpi->width;
1038 const int t = dpi->top;
1039 const int b = dpi->top + dpi->height;
1040
1041 /* The hash area to scan */
1042 int xl, xu, yl, yu;
1043
1044 if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) {
1045 xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6);
1046 xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6);
1047 } else {
1048 /* scan whole hash row */
1049 xl = 0;
1050 xu = 0x3F;
1051 }
1052
1053 if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) {
1054 yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6;
1055 yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6;
1056 } else {
1057 /* scan whole column */
1058 yl = 0;
1059 yu = 0x3F << 6;
1060 }
1061
1062 for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) {
1063 for (int x = xl;; x = (x + 1) & 0x3F) {
1064 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1065
1066 while (v != NULL) {
1067 if (!(v->vehstatus & VS_HIDDEN) &&
1068 l <= v->coord.right &&
1069 t <= v->coord.bottom &&
1070 r >= v->coord.left &&
1071 b >= v->coord.top) {
1072 DoDrawVehicle(v);
1073 }
1074 v = v->hash_viewport_next;
1075 }
1076
1077 if (x == xu) break;
1078 }
1079
1080 if (y == yu) break;
1081 }
1082 }
1083
1091 Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
1092 {
1093 Vehicle *found = NULL, *v;
1094 uint dist, best_dist = UINT_MAX;
1095
1096 if ((uint)(x -= vp->left) >= (uint)vp->width || (uint)(y -= vp->top) >= (uint)vp->height) return NULL;
1097
1098 x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
1099 y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
1100
1101 FOR_ALL_VEHICLES(v) {
1102 if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 &&
1103 x >= v->coord.left && x <= v->coord.right &&
1104 y >= v->coord.top && y <= v->coord.bottom) {
1105
1106 dist = max(
1107 abs(((v->coord.left + v->coord.right) >> 1) - x),
1108 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1109 );
1110
1111 if (dist < best_dist) {
1112 found = v;
1113 best_dist = dist;
1114 }
1115 }
1116 }
1117
1118 return found;
1119 }
1120
1125 void DecreaseVehicleValue(Vehicle *v)
1126 {
1127 v->value -= v->value >> 8;
1128 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1129 }
1130
1131 static const byte _breakdown_chance[64] = {
1132 3, 3, 3, 3, 3, 3, 3, 3,
1133 4, 4, 5, 5, 6, 6, 7, 7,
1134 8, 8, 9, 9, 10, 10, 11, 11,
1135 12, 13, 13, 13, 13, 14, 15, 16,
1136 17, 19, 21, 25, 28, 31, 34, 37,
1137 40, 44, 48, 52, 56, 60, 64, 68,
1138 72, 80, 90, 100, 110, 120, 130, 140,
1139 150, 170, 190, 210, 230, 250, 250, 250,
1140 };
1141
1142 void CheckVehicleBreakdown(Vehicle *v)
1143 {
1144 int rel, rel_old;
1145
1146 /* decrease reliability */
1147 v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1148 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1149
1150 if (v->breakdown_ctr != 0 || (v->vehstatus & VS_STOPPED) ||
1151 _settings_game.difficulty.vehicle_breakdowns < 1 ||
1152 v->cur_speed < 5 || _game_mode == GM_MENU) {
1153 return;
1154 }
1155
1156 uint32 r = Random();
1157
1158 /* increase chance of failure */
1159 int chance = v->breakdown_chance + 1;
1160 if (Chance16I(1, 25, r)) chance += 25;
1161 v->breakdown_chance = min(255, chance);
1162
1163 /* calculate reliability value to use in comparison */
1164 rel = v->reliability;
1165 if (v->type == VEH_SHIP) rel += 0x6666;
1166
1167 /* reduced breakdowns? */
1168 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1169
1170 /* check if to break down */
1171 if (_breakdown_chance[(uint)min(rel, 0xffff) >> 10] <= v->breakdown_chance) {
1172 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1173 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1174 v->breakdown_chance = 0;
1175 }
1176 }
1177
1184 bool Vehicle::HandleBreakdown()
1185 {
1186 /* Possible states for Vehicle::breakdown_ctr
1187 * 0 - vehicle is running normally
1188 * 1 - vehicle is currently broken down
1189 * 2 - vehicle is going to break down now
1190 * >2 - vehicle is counting down to the actual breakdown event */
1191 switch (this->breakdown_ctr) {
1192 case 0:
1193 return false;
1194
1195 case 2:
1196 this->breakdown_ctr = 1;
1197
1198 if (this->breakdowns_since_last_service != 255) {
1199 this->breakdowns_since_last_service++;
1200 }
1201
1202 if (this->type == VEH_AIRCRAFT) {
1203 /* Aircraft just need this flag, the rest is handled elsewhere */
1204 this->vehstatus |= VS_AIRCRAFT_BROKEN;
1205 } else {
1206 this->cur_speed = 0;
1207
1208 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1209 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1210 SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
1211 (train_or_ship ? SND_10_TRAIN_BREAKDOWN : SND_0F_VEHICLE_BREAKDOWN) :
1212 (train_or_ship ? SND_3A_COMEDY_BREAKDOWN_2 : SND_35_COMEDY_BREAKDOWN), this);
1213 }
1214
1215 if (!(this->vehstatus & VS_HIDDEN) && !HasBit(EngInfo(this->engine_type)->misc_flags, EF_NO_BREAKDOWN_SMOKE)) {
1216 EffectVehicle *u = CreateEffectVehicleRel(this, 4, 4, 5, EV_BREAKDOWN_SMOKE);
1217 if (u != NULL) u->animation_state = this->breakdown_delay * 2;
1218 }
1219 }
1220
1221 this->MarkDirty(); // Update graphics after speed is zeroed
1222 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1223 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1224
1225 /* FALL THROUGH */
1226 case 1:
1227 /* Aircraft breakdowns end only when arriving at the airport */
1228 if (this->type == VEH_AIRCRAFT) return false;
1229
1230 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1231 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1232 if (--this->breakdown_delay == 0) {
1233 this->breakdown_ctr = 0;
1234 this->MarkDirty();
1235 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1236 }
1237 }
1238 return true;
1239
1240 default:
1241 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1242 return false;
1243 }
1244 }
1245
1250 void AgeVehicle(Vehicle *v)
1251 {
1252 if (v->age < MAX_DAY) {
1253 v->age++;
1254 if (v->IsPrimaryVehicle() && v->age == VEHICLE_PROFIT_MIN_AGE + 1) GroupStatistics::VehicleReachedProfitAge(v);
1255 }
1256
1257 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1258
1259 int age = v->age - v->max_age;
1260 if (age == DAYS_IN_LEAP_YEAR * 0 || age == DAYS_IN_LEAP_YEAR * 1 ||
1261 age == DAYS_IN_LEAP_YEAR * 2 || age == DAYS_IN_LEAP_YEAR * 3 || age == DAYS_IN_LEAP_YEAR * 4) {
1262 v->reliability_spd_dec <<= 1;
1263 }
1264
1265 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1266
1267 /* Don't warn about non-primary or not ours vehicles or vehicles that are crashed */
1268 if (v->Previous() != NULL || v->owner != _local_company || (v->vehstatus & VS_CRASHED) != 0) return;
1269
1270 /* Don't warn if a renew is active */
1271 if (Company::Get(v->owner)->settings.engine_renew && v->GetEngine()->company_avail != 0) return;
1272
1273 StringID str;
1274 if (age == -DAYS_IN_LEAP_YEAR) {
1275 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1276 } else if (age == 0) {
1277 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1278 } else if (age > 0 && (age % DAYS_IN_LEAP_YEAR) == 0) {
1279 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1280 } else {
1281 return;
1282 }
1283
1284 SetDParam(0, v->index);
1285 AddVehicleAdviceNewsItem(str, v->index);
1286 }
1287
1297 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1298 {
1299 int count = 0;
1300 int max = 0;
1301 int cars = 0;
1302 int unloading = 0;
1303 bool loading = false;
1304
1305 bool is_loading = front->current_order.IsType(OT_LOADING);
1306
1307 /* The station may be NULL when the (colour) string does not need to be set. */
1308 const Station *st = Station::GetIfValid(front->last_station_visited);
1309 assert(colour == NULL || (st != NULL && is_loading));
1310
1311 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1312 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1313
1314 /* Count up max and used */
1315 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1316 count += v->cargo.StoredCount();
1317 max += v->cargo_cap;
1318 if (v->cargo_cap != 0 && colour != NULL) {
1319 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1320 loading |= !order_no_load &&
1321 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1322 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1323 cars++;
1324 }
1325 }
1326
1327 if (colour != NULL) {
1328 if (unloading == 0 && loading) {
1329 *colour = STR_PERCENT_UP;
1330 } else if (unloading == 0 && !loading) {
1331 *colour = STR_PERCENT_NONE;
1332 } else if (cars == unloading || !loading) {
1333 *colour = STR_PERCENT_DOWN;
1334 } else {
1335 *colour = STR_PERCENT_UP_DOWN;
1336 }
1337 }
1338
1339 /* Train without capacity */
1340 if (max == 0) return 100;
1341
1342 /* Return the percentage */
1343 if (count * 2 < max) {
1344 /* Less than 50%; round up, so that 0% means really empty. */
1345 return CeilDiv(count * 100, max);
1346 } else {
1347 /* More than 50%; round down, so that 100% means really full. */
1348 return (count * 100) / max;
1349 }
1350 }
1351
1356 void VehicleEnterDepot(Vehicle *v)
1357 {
1358 /* Always work with the front of the vehicle */
1359 assert(v == v->First());
1360
1361 switch (v->type) {
1362 case VEH_TRAIN: {
1363 Train *t = Train::From(v);
1364 SetWindowClassesDirty(WC_TRAINS_LIST);
1365 /* Clear path reservation */
1366 SetDepotReservation(t->tile, false);
1367 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1368
1369 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1370 t->wait_counter = 0;
1371 t->force_proceed = TFP_NONE;
1372 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1373 t->ConsistChanged(CCF_ARRANGE);
1374 break;
1375 }
1376
1377 case VEH_ROAD:
1378 SetWindowClassesDirty(WC_ROADVEH_LIST);
1379 break;
1380
1381 case VEH_SHIP: {
1382 SetWindowClassesDirty(WC_SHIPS_LIST);
1383 Ship *ship = Ship::From(v);
1384 ship->state = TRACK_BIT_DEPOT;
1385 ship->UpdateCache();
1386 ship->UpdateViewport(true, true);
1387 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1388 break;
1389 }
1390
1391 case VEH_AIRCRAFT:
1392 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1393 HandleAircraftEnterHangar(Aircraft::From(v));
1394 break;
1395 default: NOT_REACHED();
1396 }
1397 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1398
1399 if (v->type != VEH_TRAIN) {
1400 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1401 * 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 */
1402 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1403 }
1404 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1405
1406 v->vehstatus |= VS_HIDDEN;
1407 v->cur_speed = 0;
1408
1409 VehicleServiceInDepot(v);
1410
1411 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1412 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1413 v->MarkDirty();
1414
1415 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1416 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1417
1418 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1419
1420 /* Test whether we are heading for this depot. If not, do nothing.
1421 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1422 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1423 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1424 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1425 /* We are heading for another depot, keep driving. */
1426 return;
1427 }
1428
1429 if (v->current_order.IsRefit()) {
1430 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1431 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1432 cur_company.Restore();
1433
1434 if (cost.Failed()) {
1435 _vehicles_to_autoreplace[v] = false;
1436 if (v->owner == _local_company) {
1437 /* Notify the user that we stopped the vehicle */
1438 SetDParam(0, v->index);
1439 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1440 }
1441 } else if (cost.GetCost() != 0) {
1442 v->profit_this_year -= cost.GetCost() << 8;
1443 if (v->owner == _local_company) {
1444 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1445 }
1446 }
1447 }
1448
1449 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1450 /* Part of orders */
1451 v->DeleteUnreachedImplicitOrders();
1452 UpdateVehicleTimetable(v, true);
1453 v->IncrementImplicitOrderIndex();
1454 }
1455 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1456 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1457 _vehicles_to_autoreplace[v] = false;
1458 /* Invalidate last_loading_station. As the link from the station
1459 * before the stop to the station after the stop can't be predicted
1460 * we shouldn't construct it when the vehicle visits the next stop. */
1461 v->last_loading_station = INVALID_STATION;
1462 if (v->owner == _local_company) {
1463 SetDParam(0, v->index);
1464 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1465 }
1466 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1467 }
1468 v->current_order.MakeDummy();
1469 }
1470 }
1471
1472
1477 void Vehicle::UpdatePosition()
1478 {
1479 UpdateVehicleTileHash(this, false);
1480 }
1481
1487 void Vehicle::UpdateViewport(bool dirty)
1488 {
1489 int img = this->cur_image;
1490 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1491 const Sprite *spr = GetSprite(img, ST_NORMAL);
1492
1493 pt.x += spr->x_offs;
1494 pt.y += spr->y_offs;
1495
1496 UpdateVehicleViewportHash(this, pt.x, pt.y);
1497
1498 Rect old_coord = this->coord;
1499 this->coord.left = pt.x;
1500 this->coord.top = pt.y;
1501 this->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
1502 this->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
1503
1504 if (dirty) {
1505 if (old_coord.left == INVALID_COORD) {
1506 this->MarkAllViewportsDirty();
1507 } else {
1508 ::MarkAllViewportsDirty(
1509 min(old_coord.left, this->coord.left),
1510 min(old_coord.top, this->coord.top),
1511 max(old_coord.right, this->coord.right),
1512 max(old_coord.bottom, this->coord.bottom));
1513 }
1514 }
1515 }
1516
1520 void Vehicle::UpdatePositionAndViewport()
1521 {
1522 this->UpdatePosition();
1523 this->UpdateViewport(true);
1524 }
1525
1529 void Vehicle::MarkAllViewportsDirty() const
1530 {
1531 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1532 }
1533
1539 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1540 {
1541 static const int8 _delta_coord[16] = {
1542 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1543 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1544 };
1545
1546 int x = v->x_pos + _delta_coord[v->direction];
1547 int y = v->y_pos + _delta_coord[v->direction + 8];
1548
1549 GetNewVehiclePosResult gp;
1550 gp.x = x;
1551 gp.y = y;
1552 gp.old_tile = v->tile;
1553 gp.new_tile = TileVirtXY(x, y);
1554 return gp;
1555 }
1556
1557 static const Direction _new_direction_table[] = {
1558 DIR_N, DIR_NW, DIR_W,
1559 DIR_NE, DIR_SE, DIR_SW,
1560 DIR_E, DIR_SE, DIR_S
1561 };
1562
1563 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1564 {
1565 int i = 0;
1566
1567 if (y >= v->y_pos) {
1568 if (y != v->y_pos) i += 3;
1569 i += 3;
1570 }
1571
1572 if (x >= v->x_pos) {
1573 if (x != v->x_pos) i++;
1574 i++;
1575 }
1576
1577 Direction dir = v->direction;
1578
1579 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1580 if (dirdiff == DIRDIFF_SAME) return dir;
1581 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1582 }
1583
1593 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1594 {
1595 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1596 }
1597
1605 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1606 {
1607 /* Find maximum */
1608 const Vehicle *v;
1609 FOR_ALL_VEHICLES(v) {
1610 if (v->type == type && v->owner == owner) {
1611 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1612 }
1613 }
1614
1615 if (this->maxid == 0) return;
1616
1617 /* Reserving 'maxid + 2' because we need:
1618 * - space for the last item (with v->unitnumber == maxid)
1619 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1620 this->cache = CallocT<bool>(this->maxid + 2);
1621
1622 /* Fill the cache */
1623 FOR_ALL_VEHICLES(v) {
1624 if (v->type == type && v->owner == owner) {
1625 this->cache[v->unitnumber] = true;
1626 }
1627 }
1628 }
1629
1631 UnitID FreeUnitIDGenerator::NextID()
1632 {
1633 if (this->maxid <= this->curid) return ++this->curid;
1634
1635 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1636
1637 return this->curid;
1638 }
1639
1645 UnitID GetFreeUnitNumber(VehicleType type)
1646 {
1647 /* Check whether it is allowed to build another vehicle. */
1648 uint max_veh;
1649 switch (type) {
1650 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1651 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1652 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1653 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1654 default: NOT_REACHED();
1655 }
1656
1657 const Company *c = Company::Get(_current_company);
1658 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1659
1660 FreeUnitIDGenerator gen(type, _current_company);
1661
1662 return gen.NextID();
1663 }
1664
1665
1674 bool CanBuildVehicleInfrastructure(VehicleType type)
1675 {
1676 assert(IsCompanyBuildableVehicleType(type));
1677
1678 if (!Company::IsValidID(_local_company)) return false;
1679 if (!_settings_client.gui.disable_unsuitable_building) return true;
1680
1681 UnitID max;
1682 switch (type) {
1683 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1684 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1685 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1686 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1687 default: NOT_REACHED();
1688 }
1689
1690 /* We can build vehicle infrastructure when we may build the vehicle type */
1691 if (max > 0) {
1692 /* Can we actually build the vehicle type? */
1693 const Engine *e;
1694 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1695 if (HasBit(e->company_avail, _local_company)) return true;
1696 }
1697 return false;
1698 }
1699
1700 /* We should be able to build infrastructure when we have the actual vehicle type */
1701 const Vehicle *v;
1702 FOR_ALL_VEHICLES(v) {
1703 if (v->owner == _local_company && v->type == type) return true;
1704 }
1705
1706 return false;
1707 }
1708
1709
1717 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1718 {
1719 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1720 const Engine *e = Engine::Get(engine_type);
1721 switch (e->type) {
1722 default: NOT_REACHED();
1723 case VEH_TRAIN:
1724 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1725 /* Wagonoverrides use the colour scheme of the front engine.
1726 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1727 engine_type = parent_engine_type;
1728 e = Engine::Get(engine_type);
1729 /* Note: Luckily cargo_type is not needed for engines */
1730 }
1731
1732 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1733 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1734 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1735 if (!CargoSpec::Get(cargo_type)->is_freight) {
1736 if (parent_engine_type == INVALID_ENGINE) {
1737 return LS_PASSENGER_WAGON_STEAM;
1738 } else {
1739 switch (RailVehInfo(parent_engine_type)->engclass) {
1740 default: NOT_REACHED();
1741 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1742 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1743 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1744 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1745 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1746 }
1747 }
1748 } else {
1749 return LS_FREIGHT_WAGON;
1750 }
1751 } else {
1752 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1753
1754 switch (e->u.rail.engclass) {
1755 default: NOT_REACHED();
1756 case EC_STEAM: return LS_STEAM;
1757 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1758 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1759 case EC_MONORAIL: return LS_MONORAIL;
1760 case EC_MAGLEV: return LS_MAGLEV;
1761 }
1762 }
1763
1764 case VEH_ROAD:
1765 /* Always use the livery of the front */
1766 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1767 engine_type = parent_engine_type;
1768 e = Engine::Get(engine_type);
1769 cargo_type = v->First()->cargo_type;
1770 }
1771 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1772 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1773
1774 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1775 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1776 /* Tram */
1777 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1778 } else {
1779 /* Bus or truck */
1780 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1781 }
1782
1783 case VEH_SHIP:
1784 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1785 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1786 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1787
1788 case VEH_AIRCRAFT:
1789 switch (e->u.air.subtype) {
1790 case AIR_HELI: return LS_HELICOPTER;
1791 case AIR_CTOL: return LS_SMALL_PLANE;
1792 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1793 default: NOT_REACHED();
1794 }
1795 }
1796 }
1797
1807 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1808 {
1809 const Company *c = Company::Get(company);
1810 LiveryScheme scheme = LS_DEFAULT;
1811
1812 /* The default livery is always available for use, but its in_use flag determines
1813 * whether any _other_ liveries are in use. */
1814 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1815 /* Determine the livery scheme to use */
1816 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1817
1818 /* Switch back to the default scheme if the resolved scheme is not in use */
1819 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1820 }
1821
1822 return &c->livery[scheme];
1823 }
1824
1825
1826 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1827 {
1828 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1829
1830 /* Return cached value if any */
1831 if (map != PAL_NONE) return map;
1832
1833 const Engine *e = Engine::Get(engine_type);
1834
1835 /* Check if we should use the colour map callback */
1836 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1837 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1838 /* Failure means "use the default two-colour" */
1839 if (callback != CALLBACK_FAILED) {
1840 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1841 map = GB(callback, 0, 14);
1842 /* If bit 14 is set, then the company colours are applied to the
1843 * map else it's returned as-is. */
1844 if (!HasBit(callback, 14)) {
1845 /* Update cache */
1846 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1847 return map;
1848 }
1849 }
1850 }
1851
1852 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1853
1854 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1855
1856 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1857 if (!Company::IsValidID(company)) return map;
1858
1859 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1860
1861 map += livery->colour1;
1862 if (twocc) map += livery->colour2 * 16;
1863
1864 /* Update cache */
1865 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1866 return map;
1867 }
1868
1875 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1876 {
1877 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1878 }
1879
1885 PaletteID GetVehiclePalette(const Vehicle *v)
1886 {
1887 if (v->IsGroundVehicle()) {
1888 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1889 }
1890
1891 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1892 }
1893
1897 void Vehicle::DeleteUnreachedImplicitOrders()
1898 {
1899 if (this->IsGroundVehicle()) {
1900 uint16 &gv_flags = this->GetGroundVehicleFlags();
1901 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1902 /* Do not delete orders, only skip them */
1903 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1904 this->cur_implicit_order_index = this->cur_real_order_index;
1905 InvalidateVehicleOrder(this, 0);
1906 return;
1907 }
1908 }
1909
1910 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1911 while (order != NULL) {
1912 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1913
1914 if (order->IsType(OT_IMPLICIT)) {
1915 DeleteOrder(this, this->cur_implicit_order_index);
1916 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1917 order = this->GetOrder(this->cur_implicit_order_index);
1918 } else {
1919 /* Skip non-implicit orders, e.g. service-orders */
1920 order = order->next;
1921 this->cur_implicit_order_index++;
1922 }
1923
1924 /* Wrap around */
1925 if (order == NULL) {
1926 order = this->GetOrder(0);
1927 this->cur_implicit_order_index = 0;
1928 }
1929 }
1930 }
1931
1936 void Vehicle::BeginLoading()
1937 {
1938 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
1939
1940 if (this->current_order.IsType(OT_GOTO_STATION) &&
1941 this->current_order.GetDestination() == this->last_station_visited) {
1942 this->DeleteUnreachedImplicitOrders();
1943
1944 /* Now both order indices point to the destination station, and we can start loading */
1945 this->current_order.MakeLoading(true);
1946 UpdateVehicleTimetable(this, true);
1947
1948 /* Furthermore add the Non Stop flag to mark that this station
1949 * is the actual destination of the vehicle, which is (for example)
1950 * necessary to be known for HandleTrainLoading to determine
1951 * whether the train is lost or not; not marking a train lost
1952 * that arrives at random stations is bad. */
1953 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1954
1955 } else {
1956 /* We weren't scheduled to stop here. Insert an implicit order
1957 * to show that we are stopping here.
1958 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
1959 * the 'wrong' terminal when skipping orders etc. */
1960 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
1961 if (this->IsGroundVehicle() &&
1962 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
1963 in_list->GetDestination() != this->last_station_visited)) {
1964 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
1965 /* Do not create consecutive duplicates of implicit orders */
1966 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
1967 if (prev_order == NULL ||
1968 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
1969 prev_order->GetDestination() != this->last_station_visited) {
1970
1971 /* Prefer deleting implicit orders instead of inserting new ones,
1972 * so test whether the right order follows later. In case of only
1973 * implicit orders treat the last order in the list like an
1974 * explicit one, except if the overall number of orders surpasses
1975 * IMPLICIT_ORDER_ONLY_CAP. */
1976 int target_index = this->cur_implicit_order_index;
1977 bool found = false;
1978 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
1979 const Order *order = this->GetOrder(target_index);
1980 if (order == NULL) break; // No orders.
1981 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
1982 found = true;
1983 break;
1984 }
1985 target_index++;
1986 if (target_index >= this->orders.list->GetNumOrders()) {
1987 if (this->GetNumManualOrders() == 0 &&
1988 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
1989 break;
1990 }
1991 target_index = 0;
1992 }
1993 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
1994 }
1995
1996 if (found) {
1997 if (suppress_implicit_orders) {
1998 /* Skip to the found order */
1999 this->cur_implicit_order_index = target_index;
2000 InvalidateVehicleOrder(this, 0);
2001 } else {
2002 /* Delete all implicit orders up to the station we just reached */
2003 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2004 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2005 if (order->IsType(OT_IMPLICIT)) {
2006 DeleteOrder(this, this->cur_implicit_order_index);
2007 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2008 order = this->GetOrder(this->cur_implicit_order_index);
2009 } else {
2010 /* Skip non-implicit orders, e.g. service-orders */
2011 order = order->next;
2012 this->cur_implicit_order_index++;
2013 }
2014
2015 /* Wrap around */
2016 if (order == NULL) {
2017 order = this->GetOrder(0);
2018 this->cur_implicit_order_index = 0;
2019 }
2020 assert(order != NULL);
2021 }
2022 }
2023 } else if (!suppress_implicit_orders &&
2024 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2025 Order::CanAllocateItem()) {
2026 /* Insert new implicit order */
2027 Order *implicit_order = new Order();
2028 implicit_order->MakeImplicit(this->last_station_visited);
2029 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2030 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2031
2032 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2033 * Reenable it for this vehicle */
2034 uint16 &gv_flags = this->GetGroundVehicleFlags();
2035 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2036 }
2037 }
2038 }
2039 this->current_order.MakeLoading(false);
2040 }
2041
2042 if (this->last_loading_station != INVALID_STATION &&
2043 this->last_loading_station != this->last_station_visited &&
2044 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2045 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2046 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2047 }
2048
2049 PrepareUnload(this);
2050
2051 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2052 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2053 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2054 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2055
2056 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2057 this->cur_speed = 0;
2058 this->MarkDirty();
2059 }
2060
2066 void Vehicle::CancelReservation(StationID next, Station *st)
2067 {
2068 for (Vehicle *v = this; v != NULL; v = v->next) {
2069 VehicleCargoList &cargo = v->cargo;
2070 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2071 DEBUG(misc, 1, "cancelling cargo reservation");
2072 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2073 cargo.SetTransferLoadPlace(st->xy);
2074 }
2075 cargo.KeepAll();
2076 }
2077 }
2078
2083 void Vehicle::LeaveStation()
2084 {
2085 assert(this->current_order.IsType(OT_LOADING));
2086
2087 delete this->cargo_payment;
2088
2089 /* Only update the timetable if the vehicle was supposed to stop here. */
2090 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2091
2092 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2093 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2094 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2095 /* Refresh next hop stats to make sure we've done that at least once
2096 * during the stop and that refit_cap == cargo_cap for each vehicle in
2097 * the consist. */
2098 this->ResetRefitCaps();
2099 LinkRefresher::Run(this);
2100
2101 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2102 this->last_loading_station = this->last_station_visited;
2103 } else {
2104 /* if the vehicle couldn't load and had to unload or transfer everything
2105 * set the last loading station to invalid as it will leave empty. */
2106 this->last_loading_station = INVALID_STATION;
2107 }
2108 }
2109
2110 this->current_order.MakeLeaveStation();
2111 Station *st = Station::Get(this->last_station_visited);
2112 this->CancelReservation(INVALID_STATION, st);
2113 st->loading_vehicles.remove(this);
2114
2115 HideFillingPercent(&this->fill_percent_te_id);
2116
2117 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2118 /* Trigger station animation (trains only) */
2119 if (IsTileType(this->tile, MP_STATION)) {
2120 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2121 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2122 }
2123
2124 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2125 }
2126
2127 this->MarkDirty();
2128 }
2129
2133 void Vehicle::ResetRefitCaps()
2134 {
2135 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2136 }
2137
2143 void Vehicle::HandleLoading(bool mode)
2144 {
2145 switch (this->current_order.GetType()) {
2146 case OT_LOADING: {
2147 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2148
2149 /* Not the first call for this tick, or still loading */
2150 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2151
2152 this->PlayLeaveStationSound();
2153
2154 this->LeaveStation();
2155
2156 /* Only advance to next order if we just loaded at the current one */
2157 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2158 if (order == NULL ||
2159 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2160 order->GetDestination() != this->last_station_visited) {
2161 return;
2162 }
2163 break;
2164 }
2165
2166 case OT_DUMMY: break;
2167
2168 default: return;
2169 }
2170
2171 this->IncrementImplicitOrderIndex();
2172 }
2173
2178 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2179 {
2180 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2181 if (v->cargo_cap == 0) continue;
2182 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2183 if (pair == capacities.End()) {
2184 pair = capacities.Append();
2185 pair->first = v->cargo_type;
2186 pair->second = v->cargo_cap - v->cargo.StoredCount();
2187 } else {
2188 pair->second += v->cargo_cap - v->cargo.StoredCount();
2189 }
2190 }
2191 }
2192
2193 uint Vehicle::GetConsistTotalCapacity() const
2194 {
2195 uint result = 0;
2196 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2197 result += v->cargo_cap;
2198 }
2199 return result;
2200 }
2201
2208 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2209 {
2210 CommandCost ret = CheckOwnership(this->owner);
2211 if (ret.Failed()) return ret;
2212
2213 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2214 if (this->IsStoppedInDepot()) return CMD_ERROR;
2215
2216 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2217 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2218 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2219 /* We called with a different DEPOT_SERVICE setting.
2220 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2221 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2222 if (flags & DC_EXEC) {
2223 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2224 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2225 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2226 }
2227 return CommandCost();
2228 }
2229
2230 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2231 if (flags & DC_EXEC) {
2232 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2233 * then skip to the next order; effectively cancelling this forced service */
2234 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2235
2236 if (this->IsGroundVehicle()) {
2237 uint16 &gv_flags = this->GetGroundVehicleFlags();
2238 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2239 }
2240
2241 this->current_order.MakeDummy();
2242 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2243 }
2244 return CommandCost();
2245 }
2246
2247 TileIndex location;
2248 DestinationID destination;
2249 bool reverse;
2250 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};
2251 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2252
2253 if (flags & DC_EXEC) {
2254 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2255
2256 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2257 uint16 &gv_flags = this->GetGroundVehicleFlags();
2258 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2259 }
2260
2261 this->dest_tile = location;
2262 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2263 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2264 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2265
2266 /* If there is no depot in front, reverse automatically (trains only) */
2267 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2268
2269 if (this->type == VEH_AIRCRAFT) {
2270 Aircraft *a = Aircraft::From(this);
2271 if (a->state == FLYING && a->targetairport != destination) {
2272 /* The aircraft is now heading for a different hangar than the next in the orders */
2273 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2274 AircraftNextAirportPos_and_Order(a);
2275 }
2276 }
2277 }
2278
2279 return CommandCost();
2280
2281 }
2282
2287 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2288 {
2289 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2290 const Engine *e = this->GetEngine();
2291
2292 /* Evaluate properties */
2293 byte visual_effect;
2294 switch (e->type) {
2295 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2296 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2297 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2298 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2299 }
2300
2301 /* Check powered wagon / visual effect callback */
2302 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2303 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2304
2305 if (callback != CALLBACK_FAILED) {
2306 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2307
2308 callback = GB(callback, 0, 8);
2309 /* Avoid accidentally setting 'visual_effect' to the default value
2310 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2311 if (callback == VE_DEFAULT) {
2312 assert(HasBit(callback, VE_DISABLE_EFFECT));
2313 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2314 }
2315 visual_effect = callback;
2316 }
2317 }
2318
2319 /* Apply default values */
2320 if (visual_effect == VE_DEFAULT ||
2321 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2322 /* Only train engines have default effects.
2323 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2324 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2325 if (visual_effect == VE_DEFAULT) {
2326 visual_effect = 1 << VE_DISABLE_EFFECT;
2327 } else {
2328 SetBit(visual_effect, VE_DISABLE_EFFECT);
2329 }
2330 } else {
2331 if (visual_effect == VE_DEFAULT) {
2332 /* Also set the offset */
2333 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2334 }
2335 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2336 }
2337 }
2338
2339 this->vcache.cached_vis_effect = visual_effect;
2340
2341 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2342 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2343 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2344 }
2345 }
2346
2347 static const int8 _vehicle_smoke_pos[8] = {
2348 1, 1, 1, 0, -1, -1, -1, 0
2349 };
2350
2355 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2356 {
2357 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2358 if (callback == CALLBACK_FAILED) return;
2359
2360 uint count = GB(callback, 0, 2);
2361 bool auto_center = HasBit(callback, 13);
2362 bool auto_rotate = !HasBit(callback, 14);
2363
2364 int8 l_center = 0;
2365 if (auto_center) {
2366 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2367 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2368 } else {
2369 /* For trains: Compute offset from vehicle position to sprite position */
2370 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2371 }
2372
2373 Direction l_dir = v->direction;
2374 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2375 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2376
2377 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2378 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2379
2380 for (uint i = 0; i < count; i++) {
2381 uint32 reg = GetRegister(0x100 + i);
2382 uint type = GB(reg, 0, 8);
2383 int8 x = GB(reg, 8, 8);
2384 int8 y = GB(reg, 16, 8);
2385 int8 z = GB(reg, 24, 8);
2386
2387 if (auto_rotate) {
2388 int8 l = x;
2389 int8 t = y;
2390 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2391 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2392 }
2393
2394 if (type >= 0xF0) {
2395 switch (type) {
2396 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2397 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2398 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2399 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2400 default: break;
2401 }
2402 }
2403 }
2404 }
2405
2410 void Vehicle::ShowVisualEffect() const
2411 {
2412 assert(this->IsPrimaryVehicle());
2413 bool sound = false;
2414
2415 /* Do not show any smoke when:
2416 * - vehicle smoke is disabled by the player
2417 * - the vehicle is slowing down or stopped (by the player)
2418 * - the vehicle is moving very slowly
2419 */
2420 if (_settings_game.vehicle.smoke_amount == 0 ||
2421 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2422 this->cur_speed < 2) {
2423 return;
2424 }
2425
2426 /* Use the speed as limited by underground and orders. */
2427 uint max_speed = this->GetCurrentMaxSpeed();
2428
2429 if (this->type == VEH_TRAIN) {
2430 const Train *t = Train::From(this);
2431 /* For trains, do not show any smoke when:
2432 * - the train is reversing
2433 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2434 */
2435 if (HasBit(t->flags, VRF_REVERSING) ||
2436 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2437 t->cur_speed >= max_speed)) {
2438 return;
2439 }
2440 }
2441
2442 const Vehicle *v = this;
2443
2444 do {
2445 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2446 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2447 VisualEffectSpawnModel effect_model = VESM_NONE;
2448 if (advanced) {
2449 effect_offset = VE_OFFSET_CENTRE;
2450 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2451 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2452 } else {
2453 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2454 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2455 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2456 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2457 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2458 }
2459
2460 /* Show no smoke when:
2461 * - Smoke has been disabled for this vehicle
2462 * - The vehicle is not visible
2463 * - The vehicle is under a bridge
2464 * - The vehicle is on a depot tile
2465 * - The vehicle is on a tunnel tile
2466 * - The vehicle is a train engine that is currently unpowered */
2467 if (effect_model == VESM_NONE ||
2468 v->vehstatus & VS_HIDDEN ||
2469 IsBridgeAbove(v->tile) ||
2470 IsDepotTile(v->tile) ||
2471 IsTunnelTile(v->tile) ||
2472 (v->type == VEH_TRAIN &&
2473 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2474 continue;
2475 }
2476
2477 EffectVehicleType evt = EV_END;
2478 switch (effect_model) {
2479 case VESM_STEAM:
2480 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2481 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2482 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2483 * REGULATION:
2484 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2485 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2486 evt = EV_STEAM_SMOKE;
2487 }
2488 break;
2489
2490 case VESM_DIESEL: {
2491 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2492 * when smoke emission stops.
2493 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2494 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2495 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2496 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2497 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2498 * maximum speed no diesel_smoke is emitted.
2499 * REGULATION:
2500 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2501 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2502 int power_weight_effect = 0;
2503 if (v->type == VEH_TRAIN) {
2504 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2505 }
2506 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2507 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2508 evt = EV_DIESEL_SMOKE;
2509 }
2510 break;
2511 }
2512
2513 case VESM_ELECTRIC:
2514 /* Electric train's spark - more often occurs when train is departing (more load)
2515 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2516 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2517 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2518 * REGULATION:
2519 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2520 if (GB(v->tick_counter, 0, 2) == 0 &&
2521 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2522 evt = EV_ELECTRIC_SPARK;
2523 }
2524 break;
2525
2526 default:
2527 NOT_REACHED();
2528 }
2529
2530 if (evt != EV_END && advanced) {
2531 sound = true;
2532 SpawnAdvancedVisualEffect(v);
2533 } else if (evt != EV_END) {
2534 sound = true;
2535
2536 /* The effect offset is relative to a point 4 units behind the vehicle's
2537 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2538 * correction factor. */
2539 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2540
2541 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2542 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2543
2544 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2545 x = -x;
2546 y = -y;
2547 }
2548
2549 CreateEffectVehicleRel(v, x, y, 10, evt);
2550 }
2551 } while ((v = v->Next()) != NULL);
2552
2553 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2554 }
2555
2560 void Vehicle::SetNext(Vehicle *next)
2561 {
2562 assert(this != next);
2563
2564 if (this->next != NULL) {
2565 /* We had an old next vehicle. Update the first and previous pointers */
2566 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2567 v->first = this->next;
2568 }
2569 this->next->previous = NULL;
2570 }
2571
2572 this->next = next;
2573
2574 if (this->next != NULL) {
2575 /* A new next vehicle. Update the first and previous pointers */
2576 if (this->next->previous != NULL) this->next->previous->next = NULL;
2577 this->next->previous = this;
2578 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2579 v->first = this->first;
2580 }
2581 }
2582 }
2583
2589 void Vehicle::AddToShared(Vehicle *shared_chain)
2590 {
2591 assert(this->previous_shared == NULL && this->next_shared == NULL);
2592
2593 if (shared_chain->orders.list == NULL) {
2594 assert(shared_chain->previous_shared == NULL);
2595 assert(shared_chain->next_shared == NULL);
2596 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2597 }
2598
2599 this->next_shared = shared_chain->next_shared;
2600 this->previous_shared = shared_chain;
2601
2602 shared_chain->next_shared = this;
2603
2604 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2605
2606 shared_chain->orders.list->AddVehicle(this);
2607 }
2608
2612 void Vehicle::RemoveFromShared()
2613 {
2614 /* Remember if we were first and the old window number before RemoveVehicle()
2615 * as this changes first if needed. */
2616 bool were_first = (this->FirstShared() == this);
2617 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2618
2619 this->orders.list->RemoveVehicle(this);
2620
2621 if (!were_first) {
2622 /* We are not the first shared one, so only relink our previous one. */
2623 this->previous_shared->next_shared = this->NextShared();
2624 }
2625
2626 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2627
2628
2629 if (this->orders.list->GetNumVehicles() == 1) {
2630 /* When there is only one vehicle, remove the shared order list window. */
2631 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2632 InvalidateVehicleOrder(this->FirstShared(), 0);
2633 } else if (were_first) {
2634 /* If we were the first one, update to the new first one.
2635 * Note: FirstShared() is already the new first */
2636 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2637 }
2638
2639 this->next_shared = NULL;
2640 this->previous_shared = NULL;
2641 }
2642
2643 void VehiclesYearlyLoop()
2644 {
2645 Vehicle *v;
2646 FOR_ALL_VEHICLES(v) {
2647 if (v->IsPrimaryVehicle()) {
2648 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2649 Money profit = v->GetDisplayProfitThisYear();
2650 if (v->age >= 730 && profit < 0) {
2651 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2652 SetDParam(0, v->index);
2653 SetDParam(1, profit);
2654 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2655 }
2656 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2657 }
2658
2659 v->profit_last_year = v->profit_this_year;
2660 v->profit_this_year = 0;
2661 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2662 }
2663 }
2664 GroupStatistics::UpdateProfits();
2665 SetWindowClassesDirty(WC_TRAINS_LIST);
2666 SetWindowClassesDirty(WC_SHIPS_LIST);
2667 SetWindowClassesDirty(WC_ROADVEH_LIST);
2668 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2669 }
2670
2671
2681 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2682 {
2683 const Engine *e = Engine::GetIfValid(engine_type);
2684 assert(e != NULL);
2685
2686 switch (e->type) {
2687 case VEH_TRAIN:
2688 return (st->facilities & FACIL_TRAIN) != 0;
2689
2690 case VEH_ROAD:
2691 /* For road vehicles we need the vehicle to know whether it can actually
2692 * use the station, but if it doesn't have facilities for RVs it is
2693 * certainly not possible that the station can be used. */
2694 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2695
2696 case VEH_SHIP:
2697 return (st->facilities & FACIL_DOCK) != 0;
2698
2699 case VEH_AIRCRAFT:
2700 return (st->facilities & FACIL_AIRPORT) != 0 &&
2701 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2702
2703 default:
2704 return false;
2705 }
2706 }
2707
2714 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2715 {
2716 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2717
2718 return CanVehicleUseStation(v->engine_type, st);
2719 }
2720
2726 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2727 {
2728 assert(this->IsGroundVehicle());
2729 if (this->type == VEH_TRAIN) {
2730 return &Train::From(this)->gcache;
2731 } else {
2732 return &RoadVehicle::From(this)->gcache;
2733 }
2734 }
2735
2741 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2742 {
2743 assert(this->IsGroundVehicle());
2744 if (this->type == VEH_TRAIN) {
2745 return &Train::From(this)->gcache;
2746 } else {
2747 return &RoadVehicle::From(this)->gcache;
2748 }
2749 }
2750
2756 uint16 &Vehicle::GetGroundVehicleFlags()
2757 {
2758 assert(this->IsGroundVehicle());
2759 if (this->type == VEH_TRAIN) {
2760 return Train::From(this)->gv_flags;
2761 } else {
2762 return RoadVehicle::From(this)->gv_flags;
2763 }
2764 }
2765
2771 const uint16 &Vehicle::GetGroundVehicleFlags() const
2772 {
2773 assert(this->IsGroundVehicle());
2774 if (this->type == VEH_TRAIN) {
2775 return Train::From(this)->gv_flags;
2776 } else {
2777 return RoadVehicle::From(this)->gv_flags;
2778 }
2779 }
2780
2789 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2790 {
2791 if (v->type == VEH_TRAIN) {
2792 Train *u = Train::From(v);
2793 /* Only include whole vehicles, so start with the first articulated part */
2794 u = u->GetFirstEnginePart();
2795
2796 /* Include num_vehicles vehicles, not counting articulated parts */
2797 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2798 do {
2799 /* Include current vehicle in the selection. */
2800 set.Include(u->index);
2801
2802 /* If the vehicle is multiheaded, add the other part too. */
2803 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2804
2805 u = u->Next();
2806 } while (u != NULL && u->IsArticulatedPart());
2807 }
2808 }
2809 }