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