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 27222 2015-04-09 18:17:43Z planetmaker $ */
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
1294 uint8 CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1295 {
1296 int count = 0;
1297 int max = 0;
1298 int cars = 0;
1299 int unloading = 0;
1300 bool loading = false;
1301
1302 bool is_loading = front->current_order.IsType(OT_LOADING);
1303
1304 /* The station may be NULL when the (colour) string does not need to be set. */
1305 const Station *st = Station::GetIfValid(front->last_station_visited);
1306 assert(colour == NULL || (st != NULL && is_loading));
1307
1308 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1309 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1310
1311 /* Count up max and used */
1312 for (const Vehicle *v = front; v != NULL; v = v->Next()) {
1313 count += v->cargo.StoredCount();
1314 max += v->cargo_cap;
1315 if (v->cargo_cap != 0 && colour != NULL) {
1316 unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
1317 loading |= !order_no_load &&
1318 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1319 !HasBit(v->vehicle_flags, VF_LOADING_FINISHED) && !HasBit(v->vehicle_flags, VF_STOP_LOADING);
1320 cars++;
1321 }
1322 }
1323
1324 if (colour != NULL) {
1325 if (unloading == 0 && loading) {
1326 *colour = STR_PERCENT_UP;
1327 } else if (unloading == 0 && !loading) {
1328 *colour = STR_PERCENT_NONE;
1329 } else if (cars == unloading || !loading) {
1330 *colour = STR_PERCENT_DOWN;
1331 } else {
1332 *colour = STR_PERCENT_UP_DOWN;
1333 }
1334 }
1335
1336 /* Train without capacity */
1337 if (max == 0) return 100;
1338
1339 /* Return the percentage */
1340 return (count * 100) / max;
1341 }
1342
1347 void VehicleEnterDepot(Vehicle *v)
1348 {
1349 /* Always work with the front of the vehicle */
1350 assert(v == v->First());
1351
1352 switch (v->type) {
1353 case VEH_TRAIN: {
1354 Train *t = Train::From(v);
1355 SetWindowClassesDirty(WC_TRAINS_LIST);
1356 /* Clear path reservation */
1357 SetDepotReservation(t->tile, false);
1358 if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile);
1359
1360 UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner);
1361 t->wait_counter = 0;
1362 t->force_proceed = TFP_NONE;
1363 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1364 t->ConsistChanged(CCF_ARRANGE);
1365 break;
1366 }
1367
1368 case VEH_ROAD:
1369 SetWindowClassesDirty(WC_ROADVEH_LIST);
1370 break;
1371
1372 case VEH_SHIP: {
1373 SetWindowClassesDirty(WC_SHIPS_LIST);
1374 Ship *ship = Ship::From(v);
1375 ship->state = TRACK_BIT_DEPOT;
1376 ship->UpdateCache();
1377 ship->UpdateViewport(true, true);
1378 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1379 break;
1380 }
1381
1382 case VEH_AIRCRAFT:
1383 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
1384 HandleAircraftEnterHangar(Aircraft::From(v));
1385 break;
1386 default: NOT_REACHED();
1387 }
1388 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1389
1390 if (v->type != VEH_TRAIN) {
1391 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1392 * 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 */
1393 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
1394 }
1395 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
1396
1397 v->vehstatus |= VS_HIDDEN;
1398 v->cur_speed = 0;
1399
1400 VehicleServiceInDepot(v);
1401
1402 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1403 TriggerVehicle(v, VEHICLE_TRIGGER_DEPOT);
1404 v->MarkDirty();
1405
1406 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1407 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
1408
1409 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1410
1411 /* Test whether we are heading for this depot. If not, do nothing.
1412 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1413 if ((v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) &&
1414 real_order != NULL && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1415 (v->type == VEH_AIRCRAFT ? v->current_order.GetDestination() != GetStationIndex(v->tile) : v->dest_tile != v->tile)) {
1416 /* We are heading for another depot, keep driving. */
1417 return;
1418 }
1419
1420 if (v->current_order.IsRefit()) {
1421 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
1422 CommandCost cost = DoCommand(v->tile, v->index, v->current_order.GetRefitCargo() | 0xFF << 8, DC_EXEC, GetCmdRefitVeh(v));
1423 cur_company.Restore();
1424
1425 if (cost.Failed()) {
1426 _vehicles_to_autoreplace[v] = false;
1427 if (v->owner == _local_company) {
1428 /* Notify the user that we stopped the vehicle */
1429 SetDParam(0, v->index);
1430 AddVehicleAdviceNewsItem(STR_NEWS_ORDER_REFIT_FAILED, v->index);
1431 }
1432 } else if (cost.GetCost() != 0) {
1433 v->profit_this_year -= cost.GetCost() << 8;
1434 if (v->owner == _local_company) {
1435 ShowCostOrIncomeAnimation(v->x_pos, v->y_pos, v->z_pos, cost.GetCost());
1436 }
1437 }
1438 }
1439
1440 if (v->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) {
1441 /* Part of orders */
1442 v->DeleteUnreachedImplicitOrders();
1443 UpdateVehicleTimetable(v, true);
1444 v->IncrementImplicitOrderIndex();
1445 }
1446 if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
1447 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1448 _vehicles_to_autoreplace[v] = false;
1449 /* Invalidate last_loading_station. As the link from the station
1450 * before the stop to the station after the stop can't be predicted
1451 * we shouldn't construct it when the vehicle visits the next stop. */
1452 v->last_loading_station = INVALID_STATION;
1453 if (v->owner == _local_company) {
1454 SetDParam(0, v->index);
1455 AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
1456 }
1457 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1458 }
1459 v->current_order.MakeDummy();
1460 }
1461 }
1462
1463
1468 void Vehicle::UpdatePosition()
1469 {
1470 UpdateVehicleTileHash(this, false);
1471 }
1472
1478 void Vehicle::UpdateViewport(bool dirty)
1479 {
1480 int img = this->cur_image;
1481 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1482 const Sprite *spr = GetSprite(img, ST_NORMAL);
1483
1484 pt.x += spr->x_offs;
1485 pt.y += spr->y_offs;
1486
1487 UpdateVehicleViewportHash(this, pt.x, pt.y);
1488
1489 Rect old_coord = this->coord;
1490 this->coord.left = pt.x;
1491 this->coord.top = pt.y;
1492 this->coord.right = pt.x + spr->width + 2 * ZOOM_LVL_BASE;
1493 this->coord.bottom = pt.y + spr->height + 2 * ZOOM_LVL_BASE;
1494
1495 if (dirty) {
1496 if (old_coord.left == INVALID_COORD) {
1497 this->MarkAllViewportsDirty();
1498 } else {
1499 ::MarkAllViewportsDirty(
1500 min(old_coord.left, this->coord.left),
1501 min(old_coord.top, this->coord.top),
1502 max(old_coord.right, this->coord.right),
1503 max(old_coord.bottom, this->coord.bottom));
1504 }
1505 }
1506 }
1507
1511 void Vehicle::UpdatePositionAndViewport()
1512 {
1513 this->UpdatePosition();
1514 this->UpdateViewport(true);
1515 }
1516
1520 void Vehicle::MarkAllViewportsDirty() const
1521 {
1522 ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1523 }
1524
1530 GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
1531 {
1532 static const int8 _delta_coord[16] = {
1533 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1534 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1535 };
1536
1537 int x = v->x_pos + _delta_coord[v->direction];
1538 int y = v->y_pos + _delta_coord[v->direction + 8];
1539
1540 GetNewVehiclePosResult gp;
1541 gp.x = x;
1542 gp.y = y;
1543 gp.old_tile = v->tile;
1544 gp.new_tile = TileVirtXY(x, y);
1545 return gp;
1546 }
1547
1548 static const Direction _new_direction_table[] = {
1549 DIR_N, DIR_NW, DIR_W,
1550 DIR_NE, DIR_SE, DIR_SW,
1551 DIR_E, DIR_SE, DIR_S
1552 };
1553
1554 Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1555 {
1556 int i = 0;
1557
1558 if (y >= v->y_pos) {
1559 if (y != v->y_pos) i += 3;
1560 i += 3;
1561 }
1562
1563 if (x >= v->x_pos) {
1564 if (x != v->x_pos) i++;
1565 i++;
1566 }
1567
1568 Direction dir = v->direction;
1569
1570 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1571 if (dirdiff == DIRDIFF_SAME) return dir;
1572 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1573 }
1574
1584 VehicleEnterTileStatus VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
1585 {
1586 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1587 }
1588
1596 FreeUnitIDGenerator::FreeUnitIDGenerator(VehicleType type, CompanyID owner) : cache(NULL), maxid(0), curid(0)
1597 {
1598 /* Find maximum */
1599 const Vehicle *v;
1600 FOR_ALL_VEHICLES(v) {
1601 if (v->type == type && v->owner == owner) {
1602 this->maxid = max<UnitID>(this->maxid, v->unitnumber);
1603 }
1604 }
1605
1606 if (this->maxid == 0) return;
1607
1608 /* Reserving 'maxid + 2' because we need:
1609 * - space for the last item (with v->unitnumber == maxid)
1610 * - one free slot working as loop terminator in FreeUnitIDGenerator::NextID() */
1611 this->cache = CallocT<bool>(this->maxid + 2);
1612
1613 /* Fill the cache */
1614 FOR_ALL_VEHICLES(v) {
1615 if (v->type == type && v->owner == owner) {
1616 this->cache[v->unitnumber] = true;
1617 }
1618 }
1619 }
1620
1622 UnitID FreeUnitIDGenerator::NextID()
1623 {
1624 if (this->maxid <= this->curid) return ++this->curid;
1625
1626 while (this->cache[++this->curid]) { } // it will stop, we reserved more space than needed
1627
1628 return this->curid;
1629 }
1630
1636 UnitID GetFreeUnitNumber(VehicleType type)
1637 {
1638 /* Check whether it is allowed to build another vehicle. */
1639 uint max_veh;
1640 switch (type) {
1641 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1642 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1643 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1644 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1645 default: NOT_REACHED();
1646 }
1647
1648 const Company *c = Company::Get(_current_company);
1649 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1650
1651 FreeUnitIDGenerator gen(type, _current_company);
1652
1653 return gen.NextID();
1654 }
1655
1656
1665 bool CanBuildVehicleInfrastructure(VehicleType type)
1666 {
1667 assert(IsCompanyBuildableVehicleType(type));
1668
1669 if (!Company::IsValidID(_local_company)) return false;
1670 if (!_settings_client.gui.disable_unsuitable_building) return true;
1671
1672 UnitID max;
1673 switch (type) {
1674 case VEH_TRAIN: max = _settings_game.vehicle.max_trains; break;
1675 case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
1676 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1677 case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
1678 default: NOT_REACHED();
1679 }
1680
1681 /* We can build vehicle infrastructure when we may build the vehicle type */
1682 if (max > 0) {
1683 /* Can we actually build the vehicle type? */
1684 const Engine *e;
1685 FOR_ALL_ENGINES_OF_TYPE(e, type) {
1686 if (HasBit(e->company_avail, _local_company)) return true;
1687 }
1688 return false;
1689 }
1690
1691 /* We should be able to build infrastructure when we have the actual vehicle type */
1692 const Vehicle *v;
1693 FOR_ALL_VEHICLES(v) {
1694 if (v->owner == _local_company && v->type == type) return true;
1695 }
1696
1697 return false;
1698 }
1699
1700
1708 LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1709 {
1710 CargoID cargo_type = v == NULL ? (CargoID)CT_INVALID : v->cargo_type;
1711 const Engine *e = Engine::Get(engine_type);
1712 switch (e->type) {
1713 default: NOT_REACHED();
1714 case VEH_TRAIN:
1715 if (v != NULL && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1716 /* Wagonoverrides use the colour scheme of the front engine.
1717 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1718 engine_type = parent_engine_type;
1719 e = Engine::Get(engine_type);
1720 /* Note: Luckily cargo_type is not needed for engines */
1721 }
1722
1723 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1724 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1725 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1726 if (!CargoSpec::Get(cargo_type)->is_freight) {
1727 if (parent_engine_type == INVALID_ENGINE) {
1728 return LS_PASSENGER_WAGON_STEAM;
1729 } else {
1730 switch (RailVehInfo(parent_engine_type)->engclass) {
1731 default: NOT_REACHED();
1732 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1733 case EC_DIESEL: return LS_PASSENGER_WAGON_DIESEL;
1734 case EC_ELECTRIC: return LS_PASSENGER_WAGON_ELECTRIC;
1735 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1736 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1737 }
1738 }
1739 } else {
1740 return LS_FREIGHT_WAGON;
1741 }
1742 } else {
1743 bool is_mu = HasBit(e->info.misc_flags, EF_RAIL_IS_MU);
1744
1745 switch (e->u.rail.engclass) {
1746 default: NOT_REACHED();
1747 case EC_STEAM: return LS_STEAM;
1748 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1749 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1750 case EC_MONORAIL: return LS_MONORAIL;
1751 case EC_MAGLEV: return LS_MAGLEV;
1752 }
1753 }
1754
1755 case VEH_ROAD:
1756 /* Always use the livery of the front */
1757 if (v != NULL && parent_engine_type != INVALID_ENGINE) {
1758 engine_type = parent_engine_type;
1759 e = Engine::Get(engine_type);
1760 cargo_type = v->First()->cargo_type;
1761 }
1762 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1763 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1764
1765 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
1766 if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
1767 /* Tram */
1768 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
1769 } else {
1770 /* Bus or truck */
1771 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
1772 }
1773
1774 case VEH_SHIP:
1775 if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
1776 if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
1777 return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
1778
1779 case VEH_AIRCRAFT:
1780 switch (e->u.air.subtype) {
1781 case AIR_HELI: return LS_HELICOPTER;
1782 case AIR_CTOL: return LS_SMALL_PLANE;
1783 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
1784 default: NOT_REACHED();
1785 }
1786 }
1787 }
1788
1798 const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, byte livery_setting)
1799 {
1800 const Company *c = Company::Get(company);
1801 LiveryScheme scheme = LS_DEFAULT;
1802
1803 /* The default livery is always available for use, but its in_use flag determines
1804 * whether any _other_ liveries are in use. */
1805 if (c->livery[LS_DEFAULT].in_use && (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company))) {
1806 /* Determine the livery scheme to use */
1807 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
1808
1809 /* Switch back to the default scheme if the resolved scheme is not in use */
1810 if (!c->livery[scheme].in_use) scheme = LS_DEFAULT;
1811 }
1812
1813 return &c->livery[scheme];
1814 }
1815
1816
1817 static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
1818 {
1819 PaletteID map = (v != NULL) ? v->colourmap : PAL_NONE;
1820
1821 /* Return cached value if any */
1822 if (map != PAL_NONE) return map;
1823
1824 const Engine *e = Engine::Get(engine_type);
1825
1826 /* Check if we should use the colour map callback */
1827 if (HasBit(e->info.callback_mask, CBM_VEHICLE_COLOUR_REMAP)) {
1828 uint16 callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
1829 /* Failure means "use the default two-colour" */
1830 if (callback != CALLBACK_FAILED) {
1831 assert_compile(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
1832 map = GB(callback, 0, 14);
1833 /* If bit 14 is set, then the company colours are applied to the
1834 * map else it's returned as-is. */
1835 if (!HasBit(callback, 14)) {
1836 /* Update cache */
1837 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1838 return map;
1839 }
1840 }
1841 }
1842
1843 bool twocc = HasBit(e->info.misc_flags, EF_USES_2CC);
1844
1845 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
1846
1847 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
1848 if (!Company::IsValidID(company)) return map;
1849
1850 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
1851
1852 map += livery->colour1;
1853 if (twocc) map += livery->colour2 * 16;
1854
1855 /* Update cache */
1856 if (v != NULL) const_cast<Vehicle *>(v)->colourmap = map;
1857 return map;
1858 }
1859
1866 PaletteID GetEnginePalette(EngineID engine_type, CompanyID company)
1867 {
1868 return GetEngineColourMap(engine_type, company, INVALID_ENGINE, NULL);
1869 }
1870
1876 PaletteID GetVehiclePalette(const Vehicle *v)
1877 {
1878 if (v->IsGroundVehicle()) {
1879 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
1880 }
1881
1882 return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
1883 }
1884
1888 void Vehicle::DeleteUnreachedImplicitOrders()
1889 {
1890 if (this->IsGroundVehicle()) {
1891 uint16 &gv_flags = this->GetGroundVehicleFlags();
1892 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
1893 /* Do not delete orders, only skip them */
1894 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
1895 this->cur_implicit_order_index = this->cur_real_order_index;
1896 InvalidateVehicleOrder(this, 0);
1897 return;
1898 }
1899 }
1900
1901 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1902 while (order != NULL) {
1903 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
1904
1905 if (order->IsType(OT_IMPLICIT)) {
1906 DeleteOrder(this, this->cur_implicit_order_index);
1907 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1908 order = this->GetOrder(this->cur_implicit_order_index);
1909 } else {
1910 /* Skip non-implicit orders, e.g. service-orders */
1911 order = order->next;
1912 this->cur_implicit_order_index++;
1913 }
1914
1915 /* Wrap around */
1916 if (order == NULL) {
1917 order = this->GetOrder(0);
1918 this->cur_implicit_order_index = 0;
1919 }
1920 }
1921 }
1922
1927 void Vehicle::BeginLoading()
1928 {
1929 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
1930
1931 if (this->current_order.IsType(OT_GOTO_STATION) &&
1932 this->current_order.GetDestination() == this->last_station_visited) {
1933 this->DeleteUnreachedImplicitOrders();
1934
1935 /* Now both order indices point to the destination station, and we can start loading */
1936 this->current_order.MakeLoading(true);
1937 UpdateVehicleTimetable(this, true);
1938
1939 /* Furthermore add the Non Stop flag to mark that this station
1940 * is the actual destination of the vehicle, which is (for example)
1941 * necessary to be known for HandleTrainLoading to determine
1942 * whether the train is lost or not; not marking a train lost
1943 * that arrives at random stations is bad. */
1944 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
1945
1946 } else {
1947 /* We weren't scheduled to stop here. Insert an implicit order
1948 * to show that we are stopping here.
1949 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
1950 * the 'wrong' terminal when skipping orders etc. */
1951 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
1952 if (this->IsGroundVehicle() &&
1953 (in_list == NULL || !in_list->IsType(OT_IMPLICIT) ||
1954 in_list->GetDestination() != this->last_station_visited)) {
1955 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
1956 /* Do not create consecutive duplicates of implicit orders */
1957 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : NULL);
1958 if (prev_order == NULL ||
1959 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
1960 prev_order->GetDestination() != this->last_station_visited) {
1961
1962 /* Prefer deleting implicit orders instead of inserting new ones,
1963 * so test whether the right order follows later. In case of only
1964 * implicit orders treat the last order in the list like an
1965 * explicit one, except if the overall number of orders surpasses
1966 * IMPLICIT_ORDER_ONLY_CAP. */
1967 int target_index = this->cur_implicit_order_index;
1968 bool found = false;
1969 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
1970 const Order *order = this->GetOrder(target_index);
1971 if (order == NULL) break; // No orders.
1972 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
1973 found = true;
1974 break;
1975 }
1976 target_index++;
1977 if (target_index >= this->orders.list->GetNumOrders()) {
1978 if (this->GetNumManualOrders() == 0 &&
1979 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
1980 break;
1981 }
1982 target_index = 0;
1983 }
1984 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
1985 }
1986
1987 if (found) {
1988 if (suppress_implicit_orders) {
1989 /* Skip to the found order */
1990 this->cur_implicit_order_index = target_index;
1991 InvalidateVehicleOrder(this, 0);
1992 } else {
1993 /* Delete all implicit orders up to the station we just reached */
1994 const Order *order = this->GetOrder(this->cur_implicit_order_index);
1995 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
1996 if (order->IsType(OT_IMPLICIT)) {
1997 DeleteOrder(this, this->cur_implicit_order_index);
1998 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
1999 order = this->GetOrder(this->cur_implicit_order_index);
2000 } else {
2001 /* Skip non-implicit orders, e.g. service-orders */
2002 order = order->next;
2003 this->cur_implicit_order_index++;
2004 }
2005
2006 /* Wrap around */
2007 if (order == NULL) {
2008 order = this->GetOrder(0);
2009 this->cur_implicit_order_index = 0;
2010 }
2011 assert(order != NULL);
2012 }
2013 }
2014 } else if (!suppress_implicit_orders &&
2015 ((this->orders.list == NULL ? OrderList::CanAllocateItem() : this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2016 Order::CanAllocateItem()) {
2017 /* Insert new implicit order */
2018 Order *implicit_order = new Order();
2019 implicit_order->MakeImplicit(this->last_station_visited);
2020 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2021 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2022
2023 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2024 * Reenable it for this vehicle */
2025 uint16 &gv_flags = this->GetGroundVehicleFlags();
2026 ClrBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2027 }
2028 }
2029 }
2030 this->current_order.MakeLoading(false);
2031 }
2032
2033 if (this->last_loading_station != INVALID_STATION &&
2034 this->last_loading_station != this->last_station_visited &&
2035 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2036 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2037 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
2038 }
2039
2040 PrepareUnload(this);
2041
2042 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2043 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2044 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2045 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2046
2047 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2048 this->cur_speed = 0;
2049 this->MarkDirty();
2050 }
2051
2057 void Vehicle::CancelReservation(StationID next, Station *st)
2058 {
2059 for (Vehicle *v = this; v != NULL; v = v->next) {
2060 VehicleCargoList &cargo = v->cargo;
2061 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2062 DEBUG(misc, 1, "cancelling cargo reservation");
2063 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].cargo, next);
2064 cargo.SetTransferLoadPlace(st->xy);
2065 }
2066 cargo.KeepAll();
2067 }
2068 }
2069
2074 void Vehicle::LeaveStation()
2075 {
2076 assert(this->current_order.IsType(OT_LOADING));
2077
2078 delete this->cargo_payment;
2079
2080 /* Only update the timetable if the vehicle was supposed to stop here. */
2081 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2082
2083 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2084 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2085 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
2086 /* Refresh next hop stats to make sure we've done that at least once
2087 * during the stop and that refit_cap == cargo_cap for each vehicle in
2088 * the consist. */
2089 this->ResetRefitCaps();
2090 LinkRefresher::Run(this);
2091
2092 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2093 this->last_loading_station = this->last_station_visited;
2094 } else {
2095 /* if the vehicle couldn't load and had to unload or transfer everything
2096 * set the last loading station to invalid as it will leave empty. */
2097 this->last_loading_station = INVALID_STATION;
2098 }
2099 }
2100
2101 this->current_order.MakeLeaveStation();
2102 Station *st = Station::Get(this->last_station_visited);
2103 this->CancelReservation(INVALID_STATION, st);
2104 st->loading_vehicles.remove(this);
2105
2106 HideFillingPercent(&this->fill_percent_te_id);
2107
2108 if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
2109 /* Trigger station animation (trains only) */
2110 if (IsTileType(this->tile, MP_STATION)) {
2111 TriggerStationRandomisation(st, this->tile, SRT_TRAIN_DEPARTS);
2112 TriggerStationAnimation(st, this->tile, SAT_TRAIN_DEPARTS);
2113 }
2114
2115 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2116 }
2117
2118 this->MarkDirty();
2119 }
2120
2124 void Vehicle::ResetRefitCaps()
2125 {
2126 for (Vehicle *v = this; v != NULL; v = v->Next()) v->refit_cap = v->cargo_cap;
2127 }
2128
2134 void Vehicle::HandleLoading(bool mode)
2135 {
2136 switch (this->current_order.GetType()) {
2137 case OT_LOADING: {
2138 uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2139
2140 /* Not the first call for this tick, or still loading */
2141 if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) return;
2142
2143 this->PlayLeaveStationSound();
2144
2145 this->LeaveStation();
2146
2147 /* Only advance to next order if we just loaded at the current one */
2148 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2149 if (order == NULL ||
2150 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2151 order->GetDestination() != this->last_station_visited) {
2152 return;
2153 }
2154 break;
2155 }
2156
2157 case OT_DUMMY: break;
2158
2159 default: return;
2160 }
2161
2162 this->IncrementImplicitOrderIndex();
2163 }
2164
2169 void Vehicle::GetConsistFreeCapacities(SmallMap<CargoID, uint> &capacities) const
2170 {
2171 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2172 if (v->cargo_cap == 0) continue;
2173 SmallPair<CargoID, uint> *pair = capacities.Find(v->cargo_type);
2174 if (pair == capacities.End()) {
2175 pair = capacities.Append();
2176 pair->first = v->cargo_type;
2177 pair->second = v->cargo_cap - v->cargo.StoredCount();
2178 } else {
2179 pair->second += v->cargo_cap - v->cargo.StoredCount();
2180 }
2181 }
2182 }
2183
2184 uint Vehicle::GetConsistTotalCapacity() const
2185 {
2186 uint result = 0;
2187 for (const Vehicle *v = this; v != NULL; v = v->Next()) {
2188 result += v->cargo_cap;
2189 }
2190 return result;
2191 }
2192
2199 CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
2200 {
2201 CommandCost ret = CheckOwnership(this->owner);
2202 if (ret.Failed()) return ret;
2203
2204 if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
2205 if (this->IsStoppedInDepot()) return CMD_ERROR;
2206
2207 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2208 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2209 if (!!(command & DEPOT_SERVICE) == halt_in_depot) {
2210 /* We called with a different DEPOT_SERVICE setting.
2211 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2212 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2213 if (flags & DC_EXEC) {
2214 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2215 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2216 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2217 }
2218 return CommandCost();
2219 }
2220
2221 if (command & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
2222 if (flags & DC_EXEC) {
2223 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2224 * then skip to the next order; effectively cancelling this forced service */
2225 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2226
2227 if (this->IsGroundVehicle()) {
2228 uint16 &gv_flags = this->GetGroundVehicleFlags();
2229 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2230 }
2231
2232 this->current_order.MakeDummy();
2233 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2234 }
2235 return CommandCost();
2236 }
2237
2238 TileIndex location;
2239 DestinationID destination;
2240 bool reverse;
2241 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};
2242 if (!this->FindClosestDepot(&location, &destination, &reverse)) return_cmd_error(no_depot[this->type]);
2243
2244 if (flags & DC_EXEC) {
2245 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2246
2247 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2248 uint16 &gv_flags = this->GetGroundVehicleFlags();
2249 SetBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS);
2250 }
2251
2252 this->dest_tile = location;
2253 this->current_order.MakeGoToDepot(destination, ODTF_MANUAL);
2254 if (!(command & DEPOT_SERVICE)) this->current_order.SetDepotActionType(ODATFB_HALT);
2255 SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
2256
2257 /* If there is no depot in front, reverse automatically (trains only) */
2258 if (this->type == VEH_TRAIN && reverse) DoCommand(this->tile, this->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
2259
2260 if (this->type == VEH_AIRCRAFT) {
2261 Aircraft *a = Aircraft::From(this);
2262 if (a->state == FLYING && a->targetairport != destination) {
2263 /* The aircraft is now heading for a different hangar than the next in the orders */
2264 extern void AircraftNextAirportPos_and_Order(Aircraft *a);
2265 AircraftNextAirportPos_and_Order(a);
2266 }
2267 }
2268 }
2269
2270 return CommandCost();
2271
2272 }
2273
2278 void Vehicle::UpdateVisualEffect(bool allow_power_change)
2279 {
2280 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2281 const Engine *e = this->GetEngine();
2282
2283 /* Evaluate properties */
2284 byte visual_effect;
2285 switch (e->type) {
2286 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2287 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2288 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2289 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2290 }
2291
2292 /* Check powered wagon / visual effect callback */
2293 if (HasBit(e->info.callback_mask, CBM_VEHICLE_VISUAL_EFFECT)) {
2294 uint16 callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2295
2296 if (callback != CALLBACK_FAILED) {
2297 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2298
2299 callback = GB(callback, 0, 8);
2300 /* Avoid accidentally setting 'visual_effect' to the default value
2301 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2302 if (callback == VE_DEFAULT) {
2303 assert(HasBit(callback, VE_DISABLE_EFFECT));
2304 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2305 }
2306 visual_effect = callback;
2307 }
2308 }
2309
2310 /* Apply default values */
2311 if (visual_effect == VE_DEFAULT ||
2312 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2313 /* Only train engines have default effects.
2314 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2315 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2316 if (visual_effect == VE_DEFAULT) {
2317 visual_effect = 1 << VE_DISABLE_EFFECT;
2318 } else {
2319 SetBit(visual_effect, VE_DISABLE_EFFECT);
2320 }
2321 } else {
2322 if (visual_effect == VE_DEFAULT) {
2323 /* Also set the offset */
2324 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2325 }
2326 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2327 }
2328 }
2329
2330 this->vcache.cached_vis_effect = visual_effect;
2331
2332 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2333 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2334 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
2335 }
2336 }
2337
2338 static const int8 _vehicle_smoke_pos[8] = {
2339 1, 1, 1, 0, -1, -1, -1, 0
2340 };
2341
2346 static void SpawnAdvancedVisualEffect(const Vehicle *v)
2347 {
2348 uint16 callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v);
2349 if (callback == CALLBACK_FAILED) return;
2350
2351 uint count = GB(callback, 0, 2);
2352 bool auto_center = HasBit(callback, 13);
2353 bool auto_rotate = !HasBit(callback, 14);
2354
2355 int8 l_center = 0;
2356 if (auto_center) {
2357 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2358 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2359 } else {
2360 /* For trains: Compute offset from vehicle position to sprite position */
2361 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2362 }
2363
2364 Direction l_dir = v->direction;
2365 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2366 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2367
2368 int8 x_center = _vehicle_smoke_pos[l_dir] * l_center;
2369 int8 y_center = _vehicle_smoke_pos[t_dir] * l_center;
2370
2371 for (uint i = 0; i < count; i++) {
2372 uint32 reg = GetRegister(0x100 + i);
2373 uint type = GB(reg, 0, 8);
2374 int8 x = GB(reg, 8, 8);
2375 int8 y = GB(reg, 16, 8);
2376 int8 z = GB(reg, 24, 8);
2377
2378 if (auto_rotate) {
2379 int8 l = x;
2380 int8 t = y;
2381 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2382 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2383 }
2384
2385 if (type >= 0xF0) {
2386 switch (type) {
2387 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2388 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2389 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2390 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2391 default: break;
2392 }
2393 }
2394 }
2395 }
2396
2401 void Vehicle::ShowVisualEffect() const
2402 {
2403 assert(this->IsPrimaryVehicle());
2404 bool sound = false;
2405
2406 /* Do not show any smoke when:
2407 * - vehicle smoke is disabled by the player
2408 * - the vehicle is slowing down or stopped (by the player)
2409 * - the vehicle is moving very slowly
2410 */
2411 if (_settings_game.vehicle.smoke_amount == 0 ||
2412 this->vehstatus & (VS_TRAIN_SLOWING | VS_STOPPED) ||
2413 this->cur_speed < 2) {
2414 return;
2415 }
2416
2417 /* Use the speed as limited by underground and orders. */
2418 uint max_speed = this->GetCurrentMaxSpeed();
2419
2420 if (this->type == VEH_TRAIN) {
2421 const Train *t = Train::From(this);
2422 /* For trains, do not show any smoke when:
2423 * - the train is reversing
2424 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2425 */
2426 if (HasBit(t->flags, VRF_REVERSING) ||
2427 (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) &&
2428 t->cur_speed >= max_speed)) {
2429 return;
2430 }
2431 }
2432
2433 const Vehicle *v = this;
2434
2435 do {
2436 bool advanced = HasBit(v->vcache.cached_vis_effect, VE_ADVANCED_EFFECT);
2437 int effect_offset = GB(v->vcache.cached_vis_effect, VE_OFFSET_START, VE_OFFSET_COUNT) - VE_OFFSET_CENTRE;
2438 VisualEffectSpawnModel effect_model = VESM_NONE;
2439 if (advanced) {
2440 effect_offset = VE_OFFSET_CENTRE;
2441 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, 0, VE_ADVANCED_EFFECT);
2442 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2443 } else {
2444 effect_model = (VisualEffectSpawnModel)GB(v->vcache.cached_vis_effect, VE_TYPE_START, VE_TYPE_COUNT);
2445 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2446 assert_compile((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2447 assert_compile((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2448 assert_compile((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2449 }
2450
2451 /* Show no smoke when:
2452 * - Smoke has been disabled for this vehicle
2453 * - The vehicle is not visible
2454 * - The vehicle is under a bridge
2455 * - The vehicle is on a depot tile
2456 * - The vehicle is on a tunnel tile
2457 * - The vehicle is a train engine that is currently unpowered */
2458 if (effect_model == VESM_NONE ||
2459 v->vehstatus & VS_HIDDEN ||
2460 IsBridgeAbove(v->tile) ||
2461 IsDepotTile(v->tile) ||
2462 IsTunnelTile(v->tile) ||
2463 (v->type == VEH_TRAIN &&
2464 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2465 continue;
2466 }
2467
2468 EffectVehicleType evt = EV_END;
2469 switch (effect_model) {
2470 case VESM_STEAM:
2471 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2472 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2473 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2474 * REGULATION:
2475 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2476 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2477 evt = EV_STEAM_SMOKE;
2478 }
2479 break;
2480
2481 case VESM_DIESEL: {
2482 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2483 * when smoke emission stops.
2484 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2485 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2486 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2487 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2488 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2489 * maximum speed no diesel_smoke is emitted.
2490 * REGULATION:
2491 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2492 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2493 int power_weight_effect = 0;
2494 if (v->type == VEH_TRAIN) {
2495 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2496 }
2497 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2498 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2499 evt = EV_DIESEL_SMOKE;
2500 }
2501 break;
2502 }
2503
2504 case VESM_ELECTRIC:
2505 /* Electric train's spark - more often occurs when train is departing (more load)
2506 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2507 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2508 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2509 * REGULATION:
2510 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2511 if (GB(v->tick_counter, 0, 2) == 0 &&
2512 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2513 evt = EV_ELECTRIC_SPARK;
2514 }
2515 break;
2516
2517 default:
2518 NOT_REACHED();
2519 }
2520
2521 if (evt != EV_END && advanced) {
2522 sound = true;
2523 SpawnAdvancedVisualEffect(v);
2524 } else if (evt != EV_END) {
2525 sound = true;
2526
2527 /* The effect offset is relative to a point 4 units behind the vehicle's
2528 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2529 * correction factor. */
2530 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2531
2532 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2533 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2534
2535 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2536 x = -x;
2537 y = -y;
2538 }
2539
2540 CreateEffectVehicleRel(v, x, y, 10, evt);
2541 }
2542 } while ((v = v->Next()) != NULL);
2543
2544 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2545 }
2546
2551 void Vehicle::SetNext(Vehicle *next)
2552 {
2553 assert(this != next);
2554
2555 if (this->next != NULL) {
2556 /* We had an old next vehicle. Update the first and previous pointers */
2557 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2558 v->first = this->next;
2559 }
2560 this->next->previous = NULL;
2561 }
2562
2563 this->next = next;
2564
2565 if (this->next != NULL) {
2566 /* A new next vehicle. Update the first and previous pointers */
2567 if (this->next->previous != NULL) this->next->previous->next = NULL;
2568 this->next->previous = this;
2569 for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
2570 v->first = this->first;
2571 }
2572 }
2573 }
2574
2580 void Vehicle::AddToShared(Vehicle *shared_chain)
2581 {
2582 assert(this->previous_shared == NULL && this->next_shared == NULL);
2583
2584 if (shared_chain->orders.list == NULL) {
2585 assert(shared_chain->previous_shared == NULL);
2586 assert(shared_chain->next_shared == NULL);
2587 this->orders.list = shared_chain->orders.list = new OrderList(NULL, shared_chain);
2588 }
2589
2590 this->next_shared = shared_chain->next_shared;
2591 this->previous_shared = shared_chain;
2592
2593 shared_chain->next_shared = this;
2594
2595 if (this->next_shared != NULL) this->next_shared->previous_shared = this;
2596
2597 shared_chain->orders.list->AddVehicle(this);
2598 }
2599
2603 void Vehicle::RemoveFromShared()
2604 {
2605 /* Remember if we were first and the old window number before RemoveVehicle()
2606 * as this changes first if needed. */
2607 bool were_first = (this->FirstShared() == this);
2608 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2609
2610 this->orders.list->RemoveVehicle(this);
2611
2612 if (!were_first) {
2613 /* We are not the first shared one, so only relink our previous one. */
2614 this->previous_shared->next_shared = this->NextShared();
2615 }
2616
2617 if (this->next_shared != NULL) this->next_shared->previous_shared = this->previous_shared;
2618
2619
2620 if (this->orders.list->GetNumVehicles() == 1) {
2621 /* When there is only one vehicle, remove the shared order list window. */
2622 DeleteWindowById(GetWindowClassForVehicleType(this->type), vli.Pack());
2623 InvalidateVehicleOrder(this->FirstShared(), 0);
2624 } else if (were_first) {
2625 /* If we were the first one, update to the new first one.
2626 * Note: FirstShared() is already the new first */
2627 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.Pack(), this->FirstShared()->index | (1U << 31));
2628 }
2629
2630 this->next_shared = NULL;
2631 this->previous_shared = NULL;
2632 }
2633
2634 void VehiclesYearlyLoop()
2635 {
2636 Vehicle *v;
2637 FOR_ALL_VEHICLES(v) {
2638 if (v->IsPrimaryVehicle()) {
2639 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2640 Money profit = v->GetDisplayProfitThisYear();
2641 if (v->age >= 730 && profit < 0) {
2642 if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) {
2643 SetDParam(0, v->index);
2644 SetDParam(1, profit);
2645 AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_UNPROFITABLE, v->index);
2646 }
2647 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
2648 }
2649
2650 v->profit_last_year = v->profit_this_year;
2651 v->profit_this_year = 0;
2652 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
2653 }
2654 }
2655 GroupStatistics::UpdateProfits();
2656 SetWindowClassesDirty(WC_TRAINS_LIST);
2657 SetWindowClassesDirty(WC_SHIPS_LIST);
2658 SetWindowClassesDirty(WC_ROADVEH_LIST);
2659 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
2660 }
2661
2662
2672 bool CanVehicleUseStation(EngineID engine_type, const Station *st)
2673 {
2674 const Engine *e = Engine::GetIfValid(engine_type);
2675 assert(e != NULL);
2676
2677 switch (e->type) {
2678 case VEH_TRAIN:
2679 return (st->facilities & FACIL_TRAIN) != 0;
2680
2681 case VEH_ROAD:
2682 /* For road vehicles we need the vehicle to know whether it can actually
2683 * use the station, but if it doesn't have facilities for RVs it is
2684 * certainly not possible that the station can be used. */
2685 return (st->facilities & (FACIL_BUS_STOP | FACIL_TRUCK_STOP)) != 0;
2686
2687 case VEH_SHIP:
2688 return (st->facilities & FACIL_DOCK) != 0;
2689
2690 case VEH_AIRCRAFT:
2691 return (st->facilities & FACIL_AIRPORT) != 0 &&
2692 (st->airport.GetFTA()->flags & (e->u.air.subtype & AIR_CTOL ? AirportFTAClass::AIRPLANES : AirportFTAClass::HELICOPTERS)) != 0;
2693
2694 default:
2695 return false;
2696 }
2697 }
2698
2705 bool CanVehicleUseStation(const Vehicle *v, const Station *st)
2706 {
2707 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != NULL;
2708
2709 return CanVehicleUseStation(v->engine_type, st);
2710 }
2711
2717 GroundVehicleCache *Vehicle::GetGroundVehicleCache()
2718 {
2719 assert(this->IsGroundVehicle());
2720 if (this->type == VEH_TRAIN) {
2721 return &Train::From(this)->gcache;
2722 } else {
2723 return &RoadVehicle::From(this)->gcache;
2724 }
2725 }
2726
2732 const GroundVehicleCache *Vehicle::GetGroundVehicleCache() const
2733 {
2734 assert(this->IsGroundVehicle());
2735 if (this->type == VEH_TRAIN) {
2736 return &Train::From(this)->gcache;
2737 } else {
2738 return &RoadVehicle::From(this)->gcache;
2739 }
2740 }
2741
2747 uint16 &Vehicle::GetGroundVehicleFlags()
2748 {
2749 assert(this->IsGroundVehicle());
2750 if (this->type == VEH_TRAIN) {
2751 return Train::From(this)->gv_flags;
2752 } else {
2753 return RoadVehicle::From(this)->gv_flags;
2754 }
2755 }
2756
2762 const uint16 &Vehicle::GetGroundVehicleFlags() const
2763 {
2764 assert(this->IsGroundVehicle());
2765 if (this->type == VEH_TRAIN) {
2766 return Train::From(this)->gv_flags;
2767 } else {
2768 return RoadVehicle::From(this)->gv_flags;
2769 }
2770 }
2771
2780 void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8 num_vehicles)
2781 {
2782 if (v->type == VEH_TRAIN) {
2783 Train *u = Train::From(v);
2784 /* Only include whole vehicles, so start with the first articulated part */
2785 u = u->GetFirstEnginePart();
2786
2787 /* Include num_vehicles vehicles, not counting articulated parts */
2788 for (; u != NULL && num_vehicles > 0; num_vehicles--) {
2789 do {
2790 /* Include current vehicle in the selection. */
2791 set.Include(u->index);
2792
2793 /* If the vehicle is multiheaded, add the other part too. */
2794 if (u->IsMultiheaded()) set.Include(u->other_multiheaded_part->index);
2795
2796 u = u->Next();
2797 } while (u != NULL && u->IsArticulatedPart());
2798 }
2799 }
2800 }