OpenTTD Source 20250513-master-gc718858999
vehicle.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10#include "stdafx.h"
11#include "error.h"
12#include "roadveh.h"
13#include "ship.h"
14#include "spritecache.h"
15#include "timetable.h"
16#include "viewport_func.h"
17#include "news_func.h"
18#include "command_func.h"
19#include "company_func.h"
20#include "train.h"
21#include "aircraft.h"
22#include "newgrf_debug.h"
23#include "newgrf_sound.h"
24#include "newgrf_station.h"
25#include "group_gui.h"
26#include "strings_func.h"
27#include "zoom_func.h"
28#include "vehicle_func.h"
29#include "autoreplace_func.h"
30#include "autoreplace_gui.h"
31#include "station_base.h"
32#include "ai/ai.hpp"
33#include "depot_func.h"
34#include "network/network.h"
35#include "core/pool_func.hpp"
36#include "economy_base.h"
38#include "roadstop_base.h"
39#include "core/random_func.hpp"
40#include "core/backup_type.hpp"
42#include "order_backup.h"
43#include "sound_func.h"
44#include "effectvehicle_func.h"
45#include "effectvehicle_base.h"
46#include "vehiclelist.h"
47#include "bridge_map.h"
48#include "tunnel_map.h"
49#include "depot_map.h"
50#include "gamelog.h"
51#include "linkgraph/linkgraph.h"
52#include "linkgraph/refresh.h"
53#include "framerate_type.h"
54#include "autoreplace_cmd.h"
55#include "misc_cmd.h"
56#include "train_cmd.h"
57#include "vehicle_cmd.h"
58#include "newgrf_roadstop.h"
59#include "timer/timer.h"
63
64#include "table/strings.h"
65
66#include "safeguards.h"
67
68/* Number of bits in the hash to use from each vehicle coord */
69static const uint GEN_HASHX_BITS = 6;
70static const uint GEN_HASHY_BITS = 6;
71
72/* Size of each hash bucket */
73static const uint GEN_HASHX_BUCKET_BITS = 7;
74static const uint GEN_HASHY_BUCKET_BITS = 6;
75
76/* Compute hash for vehicle coord */
77static inline uint GetViewportHashX(int x)
78{
79 return GB(x, GEN_HASHX_BUCKET_BITS + ZOOM_BASE_SHIFT, GEN_HASHX_BITS);
80}
81
82static inline uint GetViewportHashY(int y)
83{
84 return GB(y, GEN_HASHY_BUCKET_BITS + ZOOM_BASE_SHIFT, GEN_HASHY_BITS) << GEN_HASHX_BITS;
85}
86
87static inline uint GetViewportHash(int x, int y)
88{
89 return GetViewportHashX(x) + GetViewportHashY(y);
90}
91
92/* Maximum size until hash repeats */
93static const uint GEN_HASHX_SIZE = 1 << (GEN_HASHX_BUCKET_BITS + GEN_HASHX_BITS + ZOOM_BASE_SHIFT);
94static const uint GEN_HASHY_SIZE = 1 << (GEN_HASHY_BUCKET_BITS + GEN_HASHY_BITS + ZOOM_BASE_SHIFT);
95
96/* Increments to reach next bucket in hash table */
97static const uint GEN_HASHX_INC = 1;
98static const uint GEN_HASHY_INC = 1 << GEN_HASHX_BITS;
99
100/* Mask to wrap-around buckets */
101static const uint GEN_HASHX_MASK = (1 << GEN_HASHX_BITS) - 1;
102static const uint GEN_HASHY_MASK = ((1 << GEN_HASHY_BITS) - 1) << GEN_HASHX_BITS;
103
104
108
109
110
114void VehicleSpriteSeq::GetBounds(Rect *bounds) const
115{
116 bounds->left = bounds->top = bounds->right = bounds->bottom = 0;
117 for (uint i = 0; i < this->count; ++i) {
118 const Sprite *spr = GetSprite(this->seq[i].sprite, SpriteType::Normal);
119 if (i == 0) {
120 bounds->left = spr->x_offs;
121 bounds->top = spr->y_offs;
122 bounds->right = spr->width + spr->x_offs - 1;
123 bounds->bottom = spr->height + spr->y_offs - 1;
124 } else {
125 if (spr->x_offs < bounds->left) bounds->left = spr->x_offs;
126 if (spr->y_offs < bounds->top) bounds->top = spr->y_offs;
127 int right = spr->width + spr->x_offs - 1;
128 int bottom = spr->height + spr->y_offs - 1;
129 if (right > bounds->right) bounds->right = right;
130 if (bottom > bounds->bottom) bounds->bottom = bottom;
131 }
132 }
133}
134
142void VehicleSpriteSeq::Draw(int x, int y, PaletteID default_pal, bool force_pal) const
143{
144 for (uint i = 0; i < this->count; ++i) {
145 PaletteID pal = force_pal || !this->seq[i].pal ? default_pal : this->seq[i].pal;
146 DrawSprite(this->seq[i].sprite, pal, x, y);
147 }
148}
149
156bool Vehicle::NeedsAutorenewing(const Company *c, bool use_renew_setting) const
157{
158 /* We can always generate the Company pointer when we have the vehicle.
159 * However this takes time and since the Company pointer is often present
160 * when this function is called then it's faster to pass the pointer as an
161 * argument rather than finding it again. */
162 assert(c == Company::Get(this->owner));
163
164 if (use_renew_setting && !c->settings.engine_renew) return false;
165 if (this->age - this->max_age < (c->settings.engine_renew_months * 30)) return false;
166
167 /* Only engines need renewing */
168 if (this->type == VEH_TRAIN && !Train::From(this)->IsEngine()) return false;
169
170 return true;
171}
172
179{
180 assert(v != nullptr);
181 SetWindowDirty(WC_VEHICLE_DETAILS, v->index); // ensure that last service date and reliability are updated
182
183 do {
188 /* Prevent vehicles from breaking down directly after exiting the depot. */
189 v->breakdown_chance /= 4;
190 if (_settings_game.difficulty.vehicle_breakdowns == 1) v->breakdown_chance = 0; // on reduced breakdown
191 v = v->Next();
192 } while (v != nullptr && v->HasEngineType());
193}
194
202{
203 /* Stopped or crashed vehicles will not move, as such making unmovable
204 * vehicles to go for service is lame. */
205 if (this->vehstatus.Any({VehState::Stopped, VehState::Crashed})) return false;
206
207 /* Are we ready for the next service cycle? */
208 const Company *c = Company::Get(this->owner);
209
210 /* Service intervals can be measured in different units, which we handle individually. */
211 if (this->ServiceIntervalIsPercent()) {
212 /* Service interval is in percents. */
213 if (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) return false;
215 /* Service interval is in minutes. */
216 if (this->date_of_last_service + (this->GetServiceInterval() * EconomyTime::DAYS_IN_ECONOMY_MONTH) >= TimerGameEconomy::date) return false;
217 } else {
218 /* Service interval is in days. */
219 if (this->date_of_last_service + this->GetServiceInterval() >= TimerGameEconomy::date) return false;
220 }
221
222 /* If we're servicing anyway, because we have not disabled servicing when
223 * there are no breakdowns or we are playing with breakdowns, bail out. */
226 return true;
227 }
228
229 /* Test whether there is some pending autoreplace.
230 * Note: We do this after the service-interval test.
231 * There are a lot more reasons for autoreplace to fail than we can test here reasonably. */
232 bool pending_replace = false;
233 Money needed_money = c->settings.engine_renew_money;
234 if (needed_money > GetAvailableMoney(c->index)) return false;
235
236 for (const Vehicle *v = this; v != nullptr; v = (v->type == VEH_TRAIN) ? Train::From(v)->GetNextUnit() : nullptr) {
237 bool replace_when_old = false;
238 EngineID new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
239
240 /* Check engine availability */
241 if (new_engine == EngineID::Invalid() || !Engine::Get(new_engine)->company_avail.Test(v->owner)) continue;
242 /* Is the vehicle old if we are not always replacing? */
243 if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
244
245 /* Check refittability */
246 CargoTypes available_cargo_types, union_mask;
247 GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
248 /* Is there anything to refit? */
249 if (union_mask != 0) {
251 CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type);
252 if (!HasAtMostOneBit(cargo_mask)) {
253 CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(new_engine);
254 if ((cargo_mask & new_engine_default_cargoes) != cargo_mask) {
255 /* We cannot refit to mixed cargoes in an automated way */
256 continue;
257 }
258 /* engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required */
259 } else {
260 /* Did the old vehicle carry anything? */
262 /* We can't refit the vehicle to carry the cargo we want */
263 if (!HasBit(available_cargo_types, cargo_type)) continue;
264 }
265 }
266 }
267
268 /* Check money.
269 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
270 pending_replace = true;
271 needed_money += 2 * Engine::Get(new_engine)->GetCost();
272 if (needed_money > GetAvailableMoney(c->index)) return false;
273 }
274
275 return pending_replace;
276}
277
284{
285 if (this->HasDepotOrder()) return false;
286 if (this->current_order.IsType(OT_LOADING)) return false;
287 if (this->current_order.IsType(OT_GOTO_DEPOT) && (this->current_order.GetDepotOrderType() & ODTFB_SERVICE) == 0) return false;
288 return NeedsServicing();
289}
290
292{
293 assert(!this->vehstatus.Test(VehState::Crashed));
294 assert(this->Previous() == nullptr); // IsPrimaryVehicle fails for free-wagon-chains
295
296 uint pass = 0;
297 /* Stop the vehicle. */
299 /* crash all wagons, and count passengers */
300 for (Vehicle *v = this; v != nullptr; v = v->Next()) {
301 /* We do not transfer reserver cargo back, so TotalCount() instead of StoredCount() */
302 if (IsCargoInClass(v->cargo_type, CargoClass::Passengers)) pass += v->cargo.TotalCount();
303 v->vehstatus.Set(VehState::Crashed);
304 v->MarkAllViewportsDirty();
305 }
306
307 /* Dirty some windows */
312
313 delete this->cargo_payment;
314 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
315
316 return RandomRange(pass + 1); // Randomise deceased passengers.
317}
318
319
328void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBug bug_type, bool critical)
329{
330 const Engine *e = Engine::Get(engine);
331 GRFConfig *grfconfig = GetGRFConfig(e->GetGRFID());
332
333 /* Missing GRF. Nothing useful can be done in this situation. */
334 if (grfconfig == nullptr) return;
335
336 if (!grfconfig->grf_bugs.Test(bug_type)) {
337 grfconfig->grf_bugs.Set(bug_type);
338 ShowErrorMessage(GetEncodedString(part1, grfconfig->GetName()),
339 GetEncodedString(part2, std::monostate{}, engine), WL_CRITICAL);
341 }
342
343 /* debug output */
344 Debug(grf, 0, "{}", StrMakeValid(GetString(part1, grfconfig->GetName())));
345
346 Debug(grf, 0, "{}", StrMakeValid(GetString(part2, std::monostate{}, engine)));
347}
348
355{
356 /* show a warning once for each engine in whole game and once for each GRF after each game load */
357 const Engine *engine = u->GetEngine();
358 uint32_t grfid = engine->grf_prop.grfid;
359 GRFConfig *grfconfig = GetGRFConfig(grfid);
360 if (_gamelog.GRFBugReverse(grfid, engine->grf_prop.local_id) || !grfconfig->grf_bugs.Test(GRFBug::VehLength)) {
361 ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GRFBug::VehLength, true);
362 }
363}
364
370{
371 this->type = type;
372 this->coord.left = INVALID_COORD;
374 this->group_id = DEFAULT_GROUP;
375 this->fill_percent_te_id = INVALID_TE_ID;
376 this->first = this;
377 this->colourmap = PAL_NONE;
378 this->cargo_age_counter = 1;
379 this->last_station_visited = StationID::Invalid();
380 this->last_loading_station = StationID::Invalid();
381}
382
383/* Size of the hash, 6 = 64 x 64, 7 = 128 x 128. Larger sizes will (in theory) reduce hash
384 * lookup times at the expense of memory usage. */
385constexpr uint TILE_HASH_BITS = 7;
386constexpr uint TILE_HASH_SIZE = 1 << TILE_HASH_BITS;
387constexpr uint TILE_HASH_MASK = TILE_HASH_SIZE - 1;
388constexpr uint TOTAL_TILE_HASH_SIZE = 1 << (TILE_HASH_BITS * 2);
389
390/* Resolution of the hash, 0 = 1*1 tile, 1 = 2*2 tiles, 2 = 4*4 tiles, etc.
391 * Profiling results show that 0 is fastest. */
392constexpr uint TILE_HASH_RES = 0;
393
397static inline uint GetTileHash1D(uint p)
398{
399 return GB(p, TILE_HASH_RES, TILE_HASH_BITS);
400}
401
405static inline uint IncTileHash1D(uint h)
406{
407 return (h + 1) & TILE_HASH_MASK;
408}
409
413static inline uint ComposeTileHash(uint hx, uint hy)
414{
415 return hx | hy << TILE_HASH_BITS;
416}
417
421static inline uint GetTileHash(uint x, uint y)
422{
424}
425
426static std::array<Vehicle *, TOTAL_TILE_HASH_SIZE> _vehicle_tile_hash{};
427
432VehiclesNearTileXY::Iterator::Iterator(int32_t x, int32_t y, uint max_dist)
433{
434 /* There are no negative tile coordinates */
435 pos_rect.left = std::max<int>(0, x - max_dist);
436 pos_rect.right = std::max<int>(0, x + max_dist);
437 pos_rect.top = std::max<int>(0, y - max_dist);
438 pos_rect.bottom = std::max<int>(0, y + max_dist);
439
440 if (2 * max_dist < TILE_HASH_MASK * TILE_SIZE) {
441 /* Hash area to scan */
442 this->hxmin = this->hx = GetTileHash1D(pos_rect.left / TILE_SIZE);
443 this->hxmax = GetTileHash1D(pos_rect.right / TILE_SIZE);
444 this->hymin = this->hy = GetTileHash1D(pos_rect.top / TILE_SIZE);
445 this->hymax = GetTileHash1D(pos_rect.bottom / TILE_SIZE);
446 } else {
447 /* Scan all */
448 this->hxmin = this->hx = 0;
449 this->hxmax = TILE_HASH_MASK;
450 this->hymin = this->hy = 0;
451 this->hymax = TILE_HASH_MASK;
452 }
453
454 this->current_veh = _vehicle_tile_hash[ComposeTileHash(this->hx, this->hy)];
455 this->SkipEmptyBuckets();
456 this->SkipFalseMatches();
457}
458
463{
464 assert(this->current_veh != nullptr);
465 this->current_veh = this->current_veh->hash_tile_next;
466 this->SkipEmptyBuckets();
467}
468
473{
474 while (this->current_veh == nullptr) {
475 if (this->hx != this->hxmax) {
476 this->hx = IncTileHash1D(this->hx);
477 } else if (this->hy != this->hymax) {
478 this->hx = this->hxmin;
479 this->hy = IncTileHash1D(this->hy);
480 } else {
481 return;
482 }
483 this->current_veh = _vehicle_tile_hash[ComposeTileHash(this->hx, this->hy)];
484 }
485}
486
491{
492 while (this->current_veh != nullptr && !this->pos_rect.Contains({this->current_veh->x_pos, this->current_veh->y_pos})) this->Increment();
493}
494
500{
501 this->current = _vehicle_tile_hash[GetTileHash(TileX(tile), TileY(tile))];
502 this->SkipFalseMatches();
503}
504
510{
511 this->current = this->current->hash_tile_next;
512}
513
518{
519 while (this->current != nullptr && this->current->tile != this->tile) this->Increment();
520}
521
528{
529 int z = GetTileMaxPixelZ(tile);
530
531 /* Value v is not safe in MP games, however, it is used to generate a local
532 * error message only (which may be different for different machines).
533 * Such a message does not affect MP synchronisation.
534 */
535 for (const Vehicle *v : VehiclesOnTile(tile)) {
536 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) continue;
537 if (v->z_pos > z) continue;
538
539 return CommandCost(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
540 }
541 return CommandCost();
542}
543
552{
553 for (TileIndex t : {tile, endtile}) {
554 /* Value v is not safe in MP games, however, it is used to generate a local
555 * error message only (which may be different for different machines).
556 * Such a message does not affect MP synchronisation.
557 */
558 for (const Vehicle *v : VehiclesOnTile(t)) {
559 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_SHIP) continue;
560 if (v == ignore) continue;
561 return CommandCost(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
562 }
563 }
564 return CommandCost();
565}
566
576{
577 /* Value v is not safe in MP games, however, it is used to generate a local
578 * error message only (which may be different for different machines).
579 * Such a message does not affect MP synchronisation.
580 */
581 for (const Vehicle *v : VehiclesOnTile(tile)) {
582 if (v->type != VEH_TRAIN) continue;
583
584 const Train *t = Train::From(v);
585 if ((t->track != track_bits) && !TracksOverlap(t->track | track_bits)) continue;
586
587 return CommandCost(STR_ERROR_TRAIN_IN_THE_WAY + v->type);
588 }
589 return CommandCost();
590}
591
592static void UpdateVehicleTileHash(Vehicle *v, bool remove)
593{
594 Vehicle **old_hash = v->hash_tile_current;
595 Vehicle **new_hash;
596
597 if (remove) {
598 new_hash = nullptr;
599 } else {
600 new_hash = &_vehicle_tile_hash[GetTileHash(TileX(v->tile), TileY(v->tile))];
601 }
602
603 if (old_hash == new_hash) return;
604
605 /* Remove from the old position in the hash table */
606 if (old_hash != nullptr) {
609 }
610
611 /* Insert vehicle at beginning of the new position in the hash table */
612 if (new_hash != nullptr) {
613 v->hash_tile_next = *new_hash;
614 if (v->hash_tile_next != nullptr) v->hash_tile_next->hash_tile_prev = &v->hash_tile_next;
615 v->hash_tile_prev = new_hash;
616 *new_hash = v;
617 }
618
619 /* Remember current hash position */
620 v->hash_tile_current = new_hash;
621}
622
623static std::array<Vehicle *, 1 << (GEN_HASHX_BITS + GEN_HASHY_BITS)> _vehicle_viewport_hash{};
624
625static void UpdateVehicleViewportHash(Vehicle *v, int x, int y, int old_x, int old_y)
626{
627 Vehicle **old_hash, **new_hash;
628
629 new_hash = (x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GetViewportHash(x, y)];
630 old_hash = (old_x == INVALID_COORD) ? nullptr : &_vehicle_viewport_hash[GetViewportHash(old_x, old_y)];
631
632 if (old_hash == new_hash) return;
633
634 /* remove from hash table? */
635 if (old_hash != nullptr) {
638 }
639
640 /* insert into hash table? */
641 if (new_hash != nullptr) {
642 v->hash_viewport_next = *new_hash;
644 v->hash_viewport_prev = new_hash;
645 *new_hash = v;
646 }
647}
648
649void ResetVehicleHash()
650{
651 for (Vehicle *v : Vehicle::Iterate()) { v->hash_tile_current = nullptr; }
652 _vehicle_viewport_hash.fill(nullptr);
653 _vehicle_tile_hash.fill(nullptr);
654}
655
656void ResetVehicleColourMap()
657{
658 for (Vehicle *v : Vehicle::Iterate()) { v->colourmap = PAL_NONE; }
659}
660
665using AutoreplaceMap = std::map<VehicleID, bool>;
666static AutoreplaceMap _vehicles_to_autoreplace;
667
668void InitializeVehicles()
669{
670 _vehicles_to_autoreplace.clear();
671 ResetVehicleHash();
672}
673
674uint CountVehiclesInChain(const Vehicle *v)
675{
676 uint count = 0;
677 do count++; while ((v = v->Next()) != nullptr);
678 return count;
679}
680
686{
687 switch (this->type) {
688 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft(); // don't count plane shadows and helicopter rotors
689 case VEH_TRAIN:
690 return !this->IsArticulatedPart() && // tenders and other articulated parts
691 !Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
692 case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
693 case VEH_SHIP: return true;
694 default: return false; // Only count company buildable vehicles
695 }
696}
697
703{
704 switch (this->type) {
705 case VEH_AIRCRAFT: return Aircraft::From(this)->IsNormalAircraft();
706 case VEH_TRAIN:
707 case VEH_ROAD:
708 case VEH_SHIP: return true;
709 default: return false;
710 }
711}
712
719{
720 return Engine::Get(this->engine_type);
721}
722
729{
730 return this->GetEngine()->GetGRF();
731}
732
738uint32_t Vehicle::GetGRFID() const
739{
740 return this->GetEngine()->GetGRFID();
741}
742
749{
750 this->date_of_last_service = std::max(this->date_of_last_service + interval, TimerGameEconomy::Date(0));
751 /* date_of_last_service_newgrf is not updated here as it must stay stable
752 * for vehicles outside of a depot. */
753}
754
763{
764 if (path_found) {
765 /* Route found, is the vehicle marked with "lost" flag? */
766 if (!this->vehicle_flags.Test(VehicleFlag::PathfinderLost)) return;
767
768 /* Clear the flag as the PF's problem was solved. */
769 this->vehicle_flags.Reset(VehicleFlag::PathfinderLost);
772 /* Delete the news item. */
774 return;
775 }
776
777 /* Were we already lost? */
778 if (this->vehicle_flags.Test(VehicleFlag::PathfinderLost)) return;
779
780 /* It is first time the problem occurred, set the "lost" flag. */
781 this->vehicle_flags.Set(VehicleFlag::PathfinderLost);
784
785 /* Unbunching data is no longer valid. */
786 this->ResetDepotUnbunching();
787
788 /* Notify user about the event. */
789 AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
791 AddVehicleAdviceNewsItem(AdviceType::VehicleLost, GetEncodedString(STR_NEWS_VEHICLE_IS_LOST, this->index), this->index);
792 }
793}
794
797{
798 if (CleaningPool()) return;
799
800 if (Station::IsValidID(this->last_station_visited)) {
801 Station *st = Station::Get(this->last_station_visited);
802 st->loading_vehicles.remove(this);
803
804 HideFillingPercent(&this->fill_percent_te_id);
805 this->CancelReservation(StationID::Invalid(), st);
806 delete this->cargo_payment;
807 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
808 }
809
810 if (this->IsEngineCountable()) {
812 if (this->IsPrimaryVehicle()) GroupStatistics::CountVehicle(this, -1);
814
815 if (this->owner == _local_company) InvalidateAutoreplaceWindow(this->engine_type, this->group_id);
817 }
818
819 Company::Get(this->owner)->freeunits[this->type].ReleaseID(this->unitnumber);
820
821 if (this->type == VEH_AIRCRAFT && this->IsPrimaryVehicle()) {
822 Aircraft *a = Aircraft::From(this);
824 if (st != nullptr) {
825 const auto &layout = st->airport.GetFTA()->layout;
826 st->airport.blocks.Reset(layout[a->previous_pos].blocks | layout[a->pos].blocks);
827 }
828 }
829
830
831 if (this->type == VEH_ROAD && this->IsPrimaryVehicle()) {
833 if (!v->vehstatus.Test(VehState::Crashed) && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END)) {
834 /* Leave the drive through roadstop, when you have not already left it. */
836 }
837
838 if (v->disaster_vehicle != VehicleID::Invalid()) ReleaseDisasterVehicle(v->disaster_vehicle);
839 }
840
841 if (this->Previous() == nullptr) {
843 }
844
845 if (this->IsPrimaryVehicle()) {
846 CloseWindowById(WC_VEHICLE_VIEW, this->index);
848 CloseWindowById(WC_VEHICLE_REFIT, this->index);
851 SetWindowDirty(WC_COMPANY, this->owner);
853 }
855
856 this->cargo.Truncate();
859
860 StopGlobalFollowVehicle(this);
861}
862
864{
865 if (CleaningPool()) {
866 this->cargo.OnCleanPool();
867 return;
868 }
869
870 /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles,
871 * it may happen that vehicle chain is deleted when visible */
872 if (!this->vehstatus.Test(VehState::Hidden)) this->MarkAllViewportsDirty();
873
874 Vehicle *v = this->Next();
875 this->SetNext(nullptr);
876
877 delete v;
878
879 UpdateVehicleTileHash(this, true);
880 UpdateVehicleViewportHash(this, INVALID_COORD, 0, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
881 if (this->type != VEH_EFFECT) {
882 DeleteVehicleNews(this->index);
883 DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index);
884 }
885}
886
892{
893 /* Vehicle should stop in the depot if it was in 'stopping' state */
894 _vehicles_to_autoreplace[v->index] = !v->vehstatus.Test(VehState::Stopped);
895
896 /* We ALWAYS set the stopped state. Even when the vehicle does not plan on
897 * stopping in the depot, so we stop it to ensure that it will not reserve
898 * the path out of the depot before we might autoreplace it to a different
899 * engine. The new engine would not own the reserved path we store that we
900 * stopped the vehicle, so autoreplace can start it again */
902}
903
908{
909 if (_game_mode != GM_NORMAL) return;
910
911 /* Run the calendar day proc for every DAY_TICKS vehicle starting at TimerGameCalendar::date_fract. */
913 Vehicle *v = Vehicle::Get(i);
914 if (v == nullptr) continue;
915 v->OnNewCalendarDay();
916 }
917}
918
925{
926 if (_game_mode != GM_NORMAL) return;
927
928 /* Run the economy day proc for every DAY_TICKS vehicle starting at TimerGameEconomy::date_fract. */
930 Vehicle *v = Vehicle::Get(i);
931 if (v == nullptr) continue;
932
933 /* Call the 32-day callback if needed */
934 if ((v->day_counter & 0x1F) == 0 && v->HasEngineType()) {
935 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_32DAY_CALLBACK, 0, 0, v->engine_type, v);
936 if (callback != CALLBACK_FAILED) {
937 if (HasBit(callback, 0)) {
938 TriggerVehicleRandomisation(v, VehicleRandomTrigger::Callback32); // Trigger vehicle trigger 10
939 }
940
941 /* After a vehicle trigger, the graphics and properties of the vehicle could change.
942 * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */
943 if (callback != 0) v->First()->MarkDirty();
944
945 if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback);
946 }
947 }
948
949 /* This is called once per day for each vehicle, but not in the first tick of the day */
950 v->OnNewEconomyDay();
951 }
952}
953
954void CallVehicleTicks()
955{
956 _vehicles_to_autoreplace.clear();
957
959
960 {
962 for (Station *st : Station::Iterate()) LoadUnloadStation(st);
963 }
968
969 for (Vehicle *v : Vehicle::Iterate()) {
970 [[maybe_unused]] VehicleID vehicle_index = v->index;
971
972 /* Vehicle could be deleted in this tick */
973 if (!v->Tick()) {
974 assert(Vehicle::Get(vehicle_index) == nullptr);
975 continue;
976 }
977
978 assert(Vehicle::Get(vehicle_index) == v);
979
980 switch (v->type) {
981 default: break;
982
983 case VEH_TRAIN:
984 case VEH_ROAD:
985 case VEH_AIRCRAFT:
986 case VEH_SHIP: {
987 Vehicle *front = v->First();
988
989 if (v->vcache.cached_cargo_age_period != 0) {
991 if (--v->cargo_age_counter == 0) {
992 v->cargo.AgeCargo();
994 }
995 }
996
997 /* Do not play any sound when crashed */
998 if (front->vehstatus.Test(VehState::Crashed)) continue;
999
1000 /* Do not play any sound when in depot or tunnel */
1001 if (v->vehstatus.Test(VehState::Hidden)) continue;
1002
1003 /* Do not play any sound when stopped */
1004 if (front->vehstatus.Test(VehState::Stopped) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue;
1005
1006 /* Update motion counter for animation purposes. */
1007 v->motion_counter += front->cur_speed;
1008
1009 /* Check vehicle type specifics */
1010 switch (v->type) {
1011 case VEH_TRAIN:
1012 if (!Train::From(v)->IsEngine()) continue;
1013 break;
1014
1015 case VEH_ROAD:
1016 if (!RoadVehicle::From(v)->IsFrontEngine()) continue;
1017 break;
1018
1019 case VEH_AIRCRAFT:
1020 if (!Aircraft::From(v)->IsNormalAircraft()) continue;
1021 break;
1022
1023 default:
1024 break;
1025 }
1026
1027 /* Play a running sound if the motion counter passes 256 (Do we not skip sounds?) */
1028 if (GB(v->motion_counter, 0, 8) < front->cur_speed) PlayVehicleSound(v, VSE_RUNNING);
1029
1030 /* Play an alternating running sound every 16 ticks */
1031 if (GB(v->tick_counter, 0, 4) == 0) {
1032 /* Play running sound when speed > 0 and not braking */
1033 bool running = (front->cur_speed > 0) && !front->vehstatus.Any({VehState::Stopped, VehState::TrainSlowing});
1035 }
1036
1037 break;
1038 }
1039 }
1040 }
1041
1043 for (auto &it : _vehicles_to_autoreplace) {
1044 Vehicle *v = Vehicle::Get(it.first);
1045 /* Autoreplace needs the current company set as the vehicle owner */
1046 cur_company.Change(v->owner);
1047
1048 /* Start vehicle if we stopped them in VehicleEnteredDepotThisTick()
1049 * We need to stop them between VehicleEnteredDepotThisTick() and here or we risk that
1050 * they are already leaving the depot again before being replaced. */
1051 if (it.second) v->vehstatus.Reset(VehState::Stopped);
1052
1053 /* Store the position of the effect as the vehicle pointer will become invalid later */
1054 int x = v->x_pos;
1055 int y = v->y_pos;
1056 int z = v->z_pos;
1057
1062
1063 if (!IsLocalCompany()) continue;
1064
1065 if (res.Succeeded()) {
1066 ShowCostOrIncomeAnimation(x, y, z, res.GetCost());
1067 continue;
1068 }
1069
1070 StringID error_message = res.GetErrorMessage();
1071 if (error_message == STR_ERROR_AUTOREPLACE_NOTHING_TO_DO || error_message == INVALID_STRING_ID) continue;
1072
1073 if (error_message == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) error_message = STR_ERROR_AUTOREPLACE_MONEY_LIMIT;
1074
1075 EncodedString headline;
1076 if (error_message == STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT) {
1077 headline = GetEncodedString(error_message, v->index);
1078 } else {
1079 headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED, v->index, error_message, std::monostate{});
1080 }
1081
1083 }
1084
1085 cur_company.Restore();
1086}
1087
1092static void DoDrawVehicle(const Vehicle *v)
1093{
1094 PaletteID pal = PAL_NONE;
1095
1097
1098 /* Check whether the vehicle shall be transparent due to the game state */
1099 bool shadowed = v->vehstatus.Test(VehState::Shadow);
1100
1101 if (v->type == VEH_EFFECT) {
1102 /* Check whether the vehicle shall be transparent/invisible due to GUI settings.
1103 * However, transparent smoke and bubbles look weird, so always hide them. */
1105 if (to != TO_INVALID && (IsTransparencySet(to) || IsInvisibilitySet(to))) return;
1106 }
1107
1109 for (uint i = 0; i < v->sprite_cache.sprite_seq.count; ++i) {
1110 PaletteID pal2 = v->sprite_cache.sprite_seq.seq[i].pal;
1111 if (!pal2 || v->vehstatus.Test(VehState::Crashed)) pal2 = pal;
1112 AddSortableSpriteToDraw(v->sprite_cache.sprite_seq.seq[i].sprite, pal2, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
1113 v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
1114 }
1116}
1117
1123{
1124 /* The bounding rectangle */
1125 const int l = dpi->left;
1126 const int r = dpi->left + dpi->width;
1127 const int t = dpi->top;
1128 const int b = dpi->top + dpi->height;
1129
1130 /* Border size of MAX_VEHICLE_PIXEL_xy */
1131 const int xb = MAX_VEHICLE_PIXEL_X * ZOOM_BASE;
1132 const int yb = MAX_VEHICLE_PIXEL_Y * ZOOM_BASE;
1133
1134 /* The hash area to scan */
1135 uint xl, xu, yl, yu;
1136
1137 if (static_cast<uint>(dpi->width + xb) < GEN_HASHX_SIZE) {
1138 xl = GetViewportHashX(l - xb);
1139 xu = GetViewportHashX(r);
1140 } else {
1141 /* scan whole hash row */
1142 xl = 0;
1143 xu = GEN_HASHX_MASK;
1144 }
1145
1146 if (static_cast<uint>(dpi->height + yb) < GEN_HASHY_SIZE) {
1147 yl = GetViewportHashY(t - yb);
1148 yu = GetViewportHashY(b);
1149 } else {
1150 /* scan whole column */
1151 yl = 0;
1152 yu = GEN_HASHY_MASK;
1153 }
1154
1155 for (uint y = yl;; y = (y + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1156 for (uint x = xl;; x = (x + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1157 const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF
1158
1159 while (v != nullptr) {
1160
1161 if (!v->vehstatus.Test(VehState::Hidden) &&
1162 l <= v->coord.right + xb &&
1163 t <= v->coord.bottom + yb &&
1164 r >= v->coord.left - xb &&
1165 b >= v->coord.top - yb)
1166 {
1167 /*
1168 * This vehicle can potentially be drawn as part of this viewport and
1169 * needs to be revalidated, as the sprite may not be correct.
1170 */
1172 VehicleSpriteSeq seq;
1173 v->GetImage(v->direction, EIT_ON_MAP, &seq);
1174
1175 if (seq.IsValid() && v->sprite_cache.sprite_seq != seq) {
1176 v->sprite_cache.sprite_seq = seq;
1177 /*
1178 * A sprite change may also result in a bounding box change,
1179 * so we need to update the bounding box again before we
1180 * check to see if the vehicle should be drawn. Note that
1181 * we can't interfere with the viewport hash at this point,
1182 * so we keep the original hash on the assumption there will
1183 * not be a significant change in the top and left coordinates
1184 * of the vehicle.
1185 */
1187
1188 }
1189
1191 }
1192
1193 if (l <= v->coord.right &&
1194 t <= v->coord.bottom &&
1195 r >= v->coord.left &&
1196 b >= v->coord.top) DoDrawVehicle(v);
1197 }
1198
1199 v = v->hash_viewport_next;
1200 }
1201
1202 if (x == xu) break;
1203 }
1204
1205 if (y == yu) break;
1206 }
1207}
1208
1216Vehicle *CheckClickOnVehicle(const Viewport &vp, int x, int y)
1217{
1218 Vehicle *found = nullptr;
1219 uint dist, best_dist = UINT_MAX;
1220
1221 x -= vp.left;
1222 y -= vp.top;
1223 if (!IsInsideMM(x, 0, vp.width) || !IsInsideMM(y, 0, vp.height)) return nullptr;
1224
1225 x = ScaleByZoom(x, vp.zoom) + vp.virtual_left;
1226 y = ScaleByZoom(y, vp.zoom) + vp.virtual_top;
1227
1228 /* Border size of MAX_VEHICLE_PIXEL_xy */
1229 const int xb = MAX_VEHICLE_PIXEL_X * ZOOM_BASE;
1230 const int yb = MAX_VEHICLE_PIXEL_Y * ZOOM_BASE;
1231
1232 /* The hash area to scan */
1233 uint xl = GetViewportHashX(x - xb);
1234 uint xu = GetViewportHashX(x);
1235 uint yl = GetViewportHashY(y - yb);
1236 uint yu = GetViewportHashY(y);
1237
1238 for (uint hy = yl;; hy = (hy + GEN_HASHY_INC) & GEN_HASHY_MASK) {
1239 for (uint hx = xl;; hx = (hx + GEN_HASHX_INC) & GEN_HASHX_MASK) {
1240 Vehicle *v = _vehicle_viewport_hash[hx + hy]; // already masked & 0xFFF
1241
1242 while (v != nullptr) {
1244 x >= v->coord.left && x <= v->coord.right &&
1245 y >= v->coord.top && y <= v->coord.bottom) {
1246
1247 dist = std::max(
1248 abs(((v->coord.left + v->coord.right) >> 1) - x),
1249 abs(((v->coord.top + v->coord.bottom) >> 1) - y)
1250 );
1251
1252 if (dist < best_dist) {
1253 found = v;
1254 best_dist = dist;
1255 }
1256 }
1257 v = v->hash_viewport_next;
1258 }
1259 if (hx == xu) break;
1260 }
1261 if (hy == yu) break;
1262 }
1263
1264 return found;
1265}
1266
1272{
1273 v->value -= v->value >> 8;
1275}
1276
1277static const uint8_t _breakdown_chance[64] = {
1278 3, 3, 3, 3, 3, 3, 3, 3,
1279 4, 4, 5, 5, 6, 6, 7, 7,
1280 8, 8, 9, 9, 10, 10, 11, 11,
1281 12, 13, 13, 13, 13, 14, 15, 16,
1282 17, 19, 21, 25, 28, 31, 34, 37,
1283 40, 44, 48, 52, 56, 60, 64, 68,
1284 72, 80, 90, 100, 110, 120, 130, 140,
1285 150, 170, 190, 210, 230, 250, 250, 250,
1286};
1287
1288void CheckVehicleBreakdown(Vehicle *v)
1289{
1290 int rel, rel_old;
1291
1292 /* decrease reliability */
1295 v->reliability = rel = std::max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
1296 if ((rel_old >> 8) != (rel >> 8)) SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
1297 }
1298
1299 if (v->breakdown_ctr != 0 || v->vehstatus.Test(VehState::Stopped) ||
1301 v->cur_speed < 5 || _game_mode == GM_MENU) {
1302 return;
1303 }
1304
1305 uint32_t r = Random();
1306
1307 /* increase chance of failure */
1308 int chance = v->breakdown_chance + 1;
1309 if (Chance16I(1, 25, r)) chance += 25;
1310 v->breakdown_chance = ClampTo<uint8_t>(chance);
1311
1312 /* calculate reliability value to use in comparison */
1313 rel = v->reliability;
1314 if (v->type == VEH_SHIP) rel += 0x6666;
1315
1316 /* reduced breakdowns? */
1317 if (_settings_game.difficulty.vehicle_breakdowns == 1) rel += 0x6666;
1318
1319 /* check if to break down */
1320 if (_breakdown_chance[ClampTo<uint16_t>(rel) >> 10] <= v->breakdown_chance) {
1321 v->breakdown_ctr = GB(r, 16, 6) + 0x3F;
1322 v->breakdown_delay = GB(r, 24, 7) + 0x80;
1323 v->breakdown_chance = 0;
1324 }
1325}
1326
1334{
1335 /* Possible states for Vehicle::breakdown_ctr
1336 * 0 - vehicle is running normally
1337 * 1 - vehicle is currently broken down
1338 * 2 - vehicle is going to break down now
1339 * >2 - vehicle is counting down to the actual breakdown event */
1340 switch (this->breakdown_ctr) {
1341 case 0:
1342 return false;
1343
1344 case 2:
1345 this->breakdown_ctr = 1;
1346
1347 if (this->breakdowns_since_last_service != 255) {
1348 this->breakdowns_since_last_service++;
1349 }
1350
1351 if (this->type == VEH_AIRCRAFT) {
1352 /* Aircraft just need this flag, the rest is handled elsewhere */
1353 this->vehstatus.Set(VehState::AircraftBroken);
1354 } else {
1355 this->cur_speed = 0;
1356
1357 if (!PlayVehicleSound(this, VSE_BREAKDOWN)) {
1358 bool train_or_ship = this->type == VEH_TRAIN || this->type == VEH_SHIP;
1359 SndPlayVehicleFx((_settings_game.game_creation.landscape != LandscapeType::Toyland) ?
1362 }
1363
1364 if (!this->vehstatus.Test(VehState::Hidden) && !EngInfo(this->engine_type)->misc_flags.Test(EngineMiscFlag::NoBreakdownSmoke)) {
1366 if (u != nullptr) u->animation_state = this->breakdown_delay * 2;
1367 }
1368 }
1369
1370 this->MarkDirty(); // Update graphics after speed is zeroed
1371 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1372 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
1373
1374 [[fallthrough]];
1375 case 1:
1376 /* Aircraft breakdowns end only when arriving at the airport */
1377 if (this->type == VEH_AIRCRAFT) return false;
1378
1379 /* For trains this function is called twice per tick, so decrease v->breakdown_delay at half the rate */
1380 if ((this->tick_counter & (this->type == VEH_TRAIN ? 3 : 1)) == 0) {
1381 if (--this->breakdown_delay == 0) {
1382 this->breakdown_ctr = 0;
1383 this->MarkDirty();
1384 SetWindowDirty(WC_VEHICLE_VIEW, this->index);
1385 }
1386 }
1387 return true;
1388
1389 default:
1390 if (!this->current_order.IsType(OT_LOADING)) this->breakdown_ctr--;
1391 return false;
1392 }
1393}
1394
1406
1412{
1413 if (v->age < CalendarTime::MAX_DATE) v->age++;
1414
1415 if (!v->IsPrimaryVehicle() && (v->type != VEH_TRAIN || !Train::From(v)->IsEngine())) return;
1416
1417 auto age = v->age - v->max_age;
1418 for (int32_t i = 0; i <= 4; i++) {
1420 v->reliability_spd_dec <<= 1;
1421 break;
1422 }
1423 }
1424
1426
1427 /* Don't warn if warnings are disabled */
1429
1430 /* Don't warn about vehicles which are non-primary (e.g., part of an articulated vehicle), don't belong to us, are crashed, or are stopped */
1431 if (v->Previous() != nullptr || v->owner != _local_company || v->vehstatus.Any({VehState::Crashed, VehState::Stopped})) return;
1432
1433 const Company *c = Company::Get(v->owner);
1434 /* Don't warn if a renew is active */
1435 if (c->settings.engine_renew && v->GetEngine()->company_avail.Any()) return;
1436 /* Don't warn if a replacement is active */
1438
1439 StringID str;
1441 str = STR_NEWS_VEHICLE_IS_GETTING_OLD;
1443 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD;
1445 str = STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND;
1446 } else {
1447 return;
1448 }
1449
1451}
1452
1462uint8_t CalcPercentVehicleFilled(const Vehicle *front, StringID *colour)
1463{
1464 int count = 0;
1465 int max = 0;
1466 int cars = 0;
1467 int unloading = 0;
1468 bool loading = false;
1469
1470 bool is_loading = front->current_order.IsType(OT_LOADING);
1471
1472 /* The station may be nullptr when the (colour) string does not need to be set. */
1474 assert(colour == nullptr || (st != nullptr && is_loading));
1475
1476 bool order_no_load = is_loading && (front->current_order.GetLoadType() & OLFB_NO_LOAD);
1477 bool order_full_load = is_loading && (front->current_order.GetLoadType() & OLFB_FULL_LOAD);
1478
1479 /* Count up max and used */
1480 for (const Vehicle *v = front; v != nullptr; v = v->Next()) {
1481 count += v->cargo.StoredCount();
1482 max += v->cargo_cap;
1483 if (v->cargo_cap != 0 && colour != nullptr) {
1484 unloading += v->vehicle_flags.Test(VehicleFlag::CargoUnloading) ? 1 : 0;
1485 loading |= !order_no_load &&
1486 (order_full_load || st->goods[v->cargo_type].HasRating()) &&
1488 cars++;
1489 }
1490 }
1491
1492 if (colour != nullptr) {
1493 if (unloading == 0 && loading) {
1494 *colour = STR_PERCENT_UP;
1495 } else if (unloading == 0 && !loading) {
1496 *colour = STR_PERCENT_NONE;
1497 } else if (cars == unloading || !loading) {
1498 *colour = STR_PERCENT_DOWN;
1499 } else {
1500 *colour = STR_PERCENT_UP_DOWN;
1501 }
1502 }
1503
1504 /* Train without capacity */
1505 if (max == 0) return 100;
1506
1507 /* Return the percentage */
1508 if (count * 2 < max) {
1509 /* Less than 50%; round up, so that 0% means really empty. */
1510 return CeilDiv(count * 100, max);
1511 } else {
1512 /* More than 50%; round down, so that 100% means really full. */
1513 return (count * 100) / max;
1514 }
1515}
1516
1522{
1523 /* Always work with the front of the vehicle */
1524 assert(v == v->First());
1525
1526 switch (v->type) {
1527 case VEH_TRAIN: {
1528 Train *t = Train::From(v);
1530 /* Clear path reservation */
1531 SetDepotReservation(t->tile, false);
1533
1535 t->wait_counter = 0;
1536 t->force_proceed = TFP_NONE;
1537 ClrBit(t->flags, VRF_TOGGLE_REVERSE);
1539 break;
1540 }
1541
1542 case VEH_ROAD:
1544 break;
1545
1546 case VEH_SHIP: {
1548 Ship *ship = Ship::From(v);
1549 ship->state = TRACK_BIT_DEPOT;
1550 ship->UpdateCache();
1551 ship->UpdateViewport(true, true);
1553 break;
1554 }
1555
1556 case VEH_AIRCRAFT:
1559 break;
1560 default: NOT_REACHED();
1561 }
1563
1564 if (v->type != VEH_TRAIN) {
1565 /* Trains update the vehicle list when the first unit enters the depot and calls VehicleEnterDepot() when the last unit enters.
1566 * 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 */
1568 }
1570
1572 v->cur_speed = 0;
1573
1575
1576 /* Store that the vehicle entered a depot this tick */
1578
1579 /* After a vehicle trigger, the graphics and properties of the vehicle could change. */
1580 TriggerVehicleRandomisation(v, VehicleRandomTrigger::Depot);
1581 v->MarkDirty();
1582
1584
1585 if (v->current_order.IsType(OT_GOTO_DEPOT)) {
1587
1588 const Order *real_order = v->GetOrder(v->cur_real_order_index);
1589
1590 /* Test whether we are heading for this depot. If not, do nothing.
1591 * Note: The target depot for nearest-/manual-depot-orders is only updated on junctions, but we want to accept every depot. */
1593 real_order != nullptr && !(real_order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) &&
1595 /* We are heading for another depot, keep driving. */
1596 return;
1597 }
1598
1599 if (v->current_order.IsRefit()) {
1600 Backup<CompanyID> cur_company(_current_company, v->owner);
1601 CommandCost cost = std::get<0>(Command<CMD_REFIT_VEHICLE>::Do(DoCommandFlag::Execute, v->index, v->current_order.GetRefitCargo(), 0xFF, false, false, 0));
1602 cur_company.Restore();
1603
1604 if (cost.Failed()) {
1605 _vehicles_to_autoreplace[v->index] = false;
1606 if (v->owner == _local_company) {
1607 /* Notify the user that we stopped the vehicle */
1608 AddVehicleAdviceNewsItem(AdviceType::RefitFailed, GetEncodedString(STR_NEWS_ORDER_REFIT_FAILED, v->index), v->index);
1609 }
1610 } else if (cost.GetCost() != 0) {
1611 v->profit_this_year -= cost.GetCost() << 8;
1612 if (v->owner == _local_company) {
1614 }
1615 }
1616 }
1617
1619 /* Part of orders */
1621 UpdateVehicleTimetable(v, true);
1623 }
1625 /* Vehicles are always stopped on entering depots. Do not restart this one. */
1626 _vehicles_to_autoreplace[v->index] = false;
1627 /* Invalidate last_loading_station. As the link from the station
1628 * before the stop to the station after the stop can't be predicted
1629 * we shouldn't construct it when the vehicle visits the next stop. */
1630 v->last_loading_station = StationID::Invalid();
1631
1632 /* Clear unbunching data. */
1634
1635 /* Announce that the vehicle is waiting to players and AIs. */
1636 if (v->owner == _local_company) {
1637 AddVehicleAdviceNewsItem(AdviceType::VehicleWaiting, GetEncodedString(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index), v->index);
1638 }
1639 AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
1640 }
1641
1642 /* If we've entered our unbunching depot, record the round trip duration. */
1645 if (v->round_trip_time == 0) {
1646 /* This might be our first round trip. */
1647 v->round_trip_time = measured_round_trip;
1648 } else {
1649 /* If we have a previous trip, smooth the effects of outlier trip calculations caused by jams or other interference. */
1650 v->round_trip_time = Clamp(measured_round_trip, (v->round_trip_time / 2), ClampTo<TimerGameTick::Ticks>(v->round_trip_time * 2));
1651 }
1652 }
1653
1655 }
1656}
1657
1658
1664{
1665 UpdateVehicleTileHash(this, false);
1666}
1667
1672void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache) const
1673{
1674 Rect new_coord;
1675 this->sprite_cache.sprite_seq.GetBounds(&new_coord);
1676
1677 Point pt = RemapCoords(this->x_pos + this->x_offs, this->y_pos + this->y_offs, this->z_pos);
1678 new_coord.left += pt.x;
1679 new_coord.top += pt.y;
1680 new_coord.right += pt.x + 2 * ZOOM_BASE;
1681 new_coord.bottom += pt.y + 2 * ZOOM_BASE;
1682
1683 if (update_cache) {
1684 /*
1685 * If the old coordinates are invalid, set the cache to the new coordinates for correct
1686 * behaviour the next time the coordinate cache is checked.
1687 */
1688 this->sprite_cache.old_coord = this->coord.left == INVALID_COORD ? new_coord : this->coord;
1689 }
1690 else {
1691 /* Extend the bounds of the existing cached bounding box so the next dirty window is correct */
1692 this->sprite_cache.old_coord.left = std::min(this->sprite_cache.old_coord.left, this->coord.left);
1693 this->sprite_cache.old_coord.top = std::min(this->sprite_cache.old_coord.top, this->coord.top);
1694 this->sprite_cache.old_coord.right = std::max(this->sprite_cache.old_coord.right, this->coord.right);
1695 this->sprite_cache.old_coord.bottom = std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom);
1696 }
1697
1698 this->coord = new_coord;
1699}
1700
1706{
1707 /* If the existing cache is invalid we should ignore it, as it will be set to the current coords by UpdateBoundingBoxCoordinates */
1708 bool ignore_cached_coords = this->sprite_cache.old_coord.left == INVALID_COORD;
1709
1710 this->UpdateBoundingBoxCoordinates(true);
1711
1712 if (ignore_cached_coords) {
1713 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, INVALID_COORD, INVALID_COORD);
1714 } else {
1715 UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top);
1716 }
1717
1718 if (dirty) {
1719 if (ignore_cached_coords) {
1720 this->sprite_cache.is_viewport_candidate = this->MarkAllViewportsDirty();
1721 } else {
1722 this->sprite_cache.is_viewport_candidate = ::MarkAllViewportsDirty(
1723 std::min(this->sprite_cache.old_coord.left, this->coord.left),
1724 std::min(this->sprite_cache.old_coord.top, this->coord.top),
1725 std::max(this->sprite_cache.old_coord.right, this->coord.right),
1726 std::max(this->sprite_cache.old_coord.bottom, this->coord.bottom));
1727 }
1728 }
1729}
1730
1735{
1736 this->UpdatePosition();
1737 this->UpdateViewport(true);
1738}
1739
1745{
1746 return ::MarkAllViewportsDirty(this->coord.left, this->coord.top, this->coord.right, this->coord.bottom);
1747}
1748
1755{
1756 static const int8_t _delta_coord[16] = {
1757 -1,-1,-1, 0, 1, 1, 1, 0, /* x */
1758 -1, 0, 1, 1, 1, 0,-1,-1, /* y */
1759 };
1760
1761 int x = v->x_pos + _delta_coord[v->direction];
1762 int y = v->y_pos + _delta_coord[v->direction + 8];
1763
1765 gp.x = x;
1766 gp.y = y;
1767 gp.old_tile = v->tile;
1768 gp.new_tile = TileVirtXY(x, y);
1769 return gp;
1770}
1771
1772static const Direction _new_direction_table[] = {
1773 DIR_N, DIR_NW, DIR_W,
1776};
1777
1778Direction GetDirectionTowards(const Vehicle *v, int x, int y)
1779{
1780 int i = 0;
1781
1782 if (y >= v->y_pos) {
1783 if (y != v->y_pos) i += 3;
1784 i += 3;
1785 }
1786
1787 if (x >= v->x_pos) {
1788 if (x != v->x_pos) i++;
1789 i++;
1790 }
1791
1792 Direction dir = v->direction;
1793
1794 DirDiff dirdiff = DirDifference(_new_direction_table[i], dir);
1795 if (dirdiff == DIRDIFF_SAME) return dir;
1796 return ChangeDir(dir, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
1797}
1798
1809{
1810 return _tile_type_procs[GetTileType(tile)]->vehicle_enter_tile_proc(v, tile, x, y);
1811}
1812
1819{
1820 for (auto it = std::begin(this->used_bitmap); it != std::end(this->used_bitmap); ++it) {
1821 BitmapStorage available = ~(*it);
1822 if (available == 0) continue;
1823 return static_cast<UnitID>(std::distance(std::begin(this->used_bitmap), it) * BITMAP_SIZE + FindFirstBit(available) + 1);
1824 }
1825 return static_cast<UnitID>(this->used_bitmap.size() * BITMAP_SIZE + 1);
1826}
1827
1834{
1835 if (index == 0 || index == UINT16_MAX) return index;
1836
1837 index--;
1838
1839 size_t slot = index / BITMAP_SIZE;
1840 if (slot >= this->used_bitmap.size()) this->used_bitmap.resize(slot + 1);
1841 SetBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
1842
1843 return index + 1;
1844}
1845
1851{
1852 if (index == 0 || index == UINT16_MAX) return;
1853
1854 index--;
1855
1856 assert(index / BITMAP_SIZE < this->used_bitmap.size());
1857 ClrBit(this->used_bitmap[index / BITMAP_SIZE], index % BITMAP_SIZE);
1858}
1859
1866{
1867 /* Check whether it is allowed to build another vehicle. */
1868 uint max_veh;
1869 switch (type) {
1870 case VEH_TRAIN: max_veh = _settings_game.vehicle.max_trains; break;
1871 case VEH_ROAD: max_veh = _settings_game.vehicle.max_roadveh; break;
1872 case VEH_SHIP: max_veh = _settings_game.vehicle.max_ships; break;
1873 case VEH_AIRCRAFT: max_veh = _settings_game.vehicle.max_aircraft; break;
1874 default: NOT_REACHED();
1875 }
1876
1878 if (c->group_all[type].num_vehicle >= max_veh) return UINT16_MAX; // Currently already at the limit, no room to make a new one.
1879
1880 return c->freeunits[type].NextID();
1881}
1882
1883
1893{
1894 assert(IsCompanyBuildableVehicleType(type));
1895
1896 if (!Company::IsValidID(_local_company)) return false;
1897
1898 UnitID max;
1899 switch (type) {
1900 case VEH_TRAIN:
1901 if (!HasAnyRailTypesAvail(_local_company)) return false;
1903 break;
1904 case VEH_ROAD:
1905 if (!HasAnyRoadTypesAvail(_local_company, (RoadTramType)subtype)) return false;
1907 break;
1908 case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
1910 default: NOT_REACHED();
1911 }
1912
1913 /* We can build vehicle infrastructure when we may build the vehicle type */
1914 if (max > 0) {
1915 /* Can we actually build the vehicle type? */
1916 for (const Engine *e : Engine::IterateType(type)) {
1917 if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue;
1918 if (e->company_avail.Test(_local_company)) return true;
1919 }
1920 return false;
1921 }
1922
1923 /* We should be able to build infrastructure when we have the actual vehicle type */
1924 for (const Vehicle *v : Vehicle::Iterate()) {
1925 if (v->type == VEH_ROAD && GetRoadTramType(RoadVehicle::From(v)->roadtype) != (RoadTramType)subtype) continue;
1926 if (v->owner == _local_company && v->type == type) return true;
1927 }
1928
1929 return false;
1930}
1931
1932
1940LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
1941{
1942 CargoType cargo_type = v == nullptr ? INVALID_CARGO : v->cargo_type;
1943 const Engine *e = Engine::Get(engine_type);
1944 switch (e->type) {
1945 default: NOT_REACHED();
1946 case VEH_TRAIN:
1947 if (v != nullptr && parent_engine_type != EngineID::Invalid() && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
1948 /* Wagonoverrides use the colour scheme of the front engine.
1949 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
1950 engine_type = parent_engine_type;
1951 e = Engine::Get(engine_type);
1952 /* Note: Luckily cargo_type is not needed for engines */
1953 }
1954
1955 if (!IsValidCargoType(cargo_type)) cargo_type = e->GetDefaultCargoType();
1956 if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
1957 assert(IsValidCargoType(cargo_type));
1958 if (e->u.rail.railveh_type == RAILVEH_WAGON) {
1959 if (!CargoSpec::Get(cargo_type)->is_freight) {
1960 if (parent_engine_type == EngineID::Invalid()) {
1961 return LS_PASSENGER_WAGON_STEAM;
1962 } else {
1963 bool is_mu = EngInfo(parent_engine_type)->misc_flags.Test(EngineMiscFlag::RailIsMU);
1964 switch (RailVehInfo(parent_engine_type)->engclass) {
1965 default: NOT_REACHED();
1966 case EC_STEAM: return LS_PASSENGER_WAGON_STEAM;
1967 case EC_DIESEL: return is_mu ? LS_DMU : LS_PASSENGER_WAGON_DIESEL;
1968 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_PASSENGER_WAGON_ELECTRIC;
1969 case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
1970 case EC_MAGLEV: return LS_PASSENGER_WAGON_MAGLEV;
1971 }
1972 }
1973 } else {
1974 return LS_FREIGHT_WAGON;
1975 }
1976 } else {
1977 bool is_mu = e->info.misc_flags.Test(EngineMiscFlag::RailIsMU);
1978
1979 switch (e->u.rail.engclass) {
1980 default: NOT_REACHED();
1981 case EC_STEAM: return LS_STEAM;
1982 case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
1983 case EC_ELECTRIC: return is_mu ? LS_EMU : LS_ELECTRIC;
1984 case EC_MONORAIL: return LS_MONORAIL;
1985 case EC_MAGLEV: return LS_MAGLEV;
1986 }
1987 }
1988
1989 case VEH_ROAD:
1990 /* Always use the livery of the front */
1991 if (v != nullptr && parent_engine_type != EngineID::Invalid()) {
1992 engine_type = parent_engine_type;
1993 e = Engine::Get(engine_type);
1994 cargo_type = v->First()->cargo_type;
1995 }
1996 if (!IsValidCargoType(cargo_type)) cargo_type = e->GetDefaultCargoType();
1997 if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
1998 assert(IsValidCargoType(cargo_type));
1999
2000 /* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
2002 /* Tram */
2003 return IsCargoInClass(cargo_type, CargoClass::Passengers) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
2004 } else {
2005 /* Bus or truck */
2006 return IsCargoInClass(cargo_type, CargoClass::Passengers) ? LS_BUS : LS_TRUCK;
2007 }
2008
2009 case VEH_SHIP:
2010 if (!IsValidCargoType(cargo_type)) cargo_type = e->GetDefaultCargoType();
2011 if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
2012 assert(IsValidCargoType(cargo_type));
2013 return IsCargoInClass(cargo_type, CargoClass::Passengers) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
2014
2015 case VEH_AIRCRAFT:
2016 switch (e->u.air.subtype) {
2017 case AIR_HELI: return LS_HELICOPTER;
2018 case AIR_CTOL: return LS_SMALL_PLANE;
2019 case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
2020 default: NOT_REACHED();
2021 }
2022 }
2023}
2024
2034const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, uint8_t livery_setting)
2035{
2036 const Company *c = Company::Get(company);
2037 LiveryScheme scheme = LS_DEFAULT;
2038
2039 if (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company)) {
2040 if (v != nullptr) {
2041 const Group *g = Group::GetIfValid(v->First()->group_id);
2042 if (g != nullptr) {
2043 /* Traverse parents until we find a livery or reach the top */
2044 while (g->livery.in_use == 0 && g->parent != GroupID::Invalid()) {
2045 g = Group::Get(g->parent);
2046 }
2047 if (g->livery.in_use != 0) return &g->livery;
2048 }
2049 }
2050
2051 /* The default livery is always available for use, but its in_use flag determines
2052 * whether any _other_ liveries are in use. */
2053 if (c->livery[LS_DEFAULT].in_use != 0) {
2054 /* Determine the livery scheme to use */
2055 scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
2056 }
2057 }
2058
2059 return &c->livery[scheme];
2060}
2061
2062
2063static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v)
2064{
2065 PaletteID map = (v != nullptr) ? v->colourmap : PAL_NONE;
2066
2067 /* Return cached value if any */
2068 if (map != PAL_NONE) return map;
2069
2070 const Engine *e = Engine::Get(engine_type);
2071
2072 /* Check if we should use the colour map callback */
2074 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
2075 /* Failure means "use the default two-colour" */
2076 if (callback != CALLBACK_FAILED) {
2077 static_assert(PAL_NONE == 0); // Returning 0x4000 (resp. 0xC000) coincidences with default value (PAL_NONE)
2078 map = GB(callback, 0, 14);
2079 /* If bit 14 is set, then the company colours are applied to the
2080 * map else it's returned as-is. */
2081 if (!HasBit(callback, 14)) {
2082 /* Update cache */
2083 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2084 return map;
2085 }
2086 }
2087 }
2088
2089 bool twocc = e->info.misc_flags.Test(EngineMiscFlag::Uses2CC);
2090
2091 if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
2092
2093 /* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
2094 if (!Company::IsValidID(company)) return map;
2095
2096 const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
2097
2098 map += livery->colour1;
2099 if (twocc) map += livery->colour2 * 16;
2100
2101 /* Update cache */
2102 if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
2103 return map;
2104}
2105
2113{
2114 return GetEngineColourMap(engine_type, company, EngineID::Invalid(), nullptr);
2115}
2116
2123{
2124 if (v->IsGroundVehicle()) {
2125 return GetEngineColourMap(v->engine_type, v->owner, v->GetGroundVehicleCache()->first_engine, v);
2126 }
2127
2128 return GetEngineColourMap(v->engine_type, v->owner, EngineID::Invalid(), v);
2129}
2130
2135{
2136 if (this->IsGroundVehicle()) {
2137 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2138 if (HasBit(gv_flags, GVF_SUPPRESS_IMPLICIT_ORDERS)) {
2139 /* Do not delete orders, only skip them */
2141 this->cur_implicit_order_index = this->cur_real_order_index;
2142 InvalidateVehicleOrder(this, 0);
2143 return;
2144 }
2145 }
2146
2147 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2148 while (order != nullptr) {
2149 if (this->cur_implicit_order_index == this->cur_real_order_index) break;
2150
2151 if (order->IsType(OT_IMPLICIT)) {
2152 DeleteOrder(this, this->cur_implicit_order_index);
2153 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2154 order = this->GetOrder(this->cur_implicit_order_index);
2155 } else {
2156 /* Skip non-implicit orders, e.g. service-orders */
2157 order = order->next;
2158 this->cur_implicit_order_index++;
2159 }
2160
2161 /* Wrap around */
2162 if (order == nullptr) {
2163 order = this->GetOrder(0);
2164 this->cur_implicit_order_index = 0;
2165 }
2166 }
2167}
2168
2174{
2175 assert(IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP);
2176
2177 TimerGameTick::Ticks travel_time = TimerGameTick::counter - this->last_loading_tick;
2178 if (this->current_order.IsType(OT_GOTO_STATION) &&
2179 this->current_order.GetDestination() == this->last_station_visited) {
2180 this->DeleteUnreachedImplicitOrders();
2181
2182 /* Now both order indices point to the destination station, and we can start loading */
2183 this->current_order.MakeLoading(true);
2184 UpdateVehicleTimetable(this, true);
2185
2186 /* Furthermore add the Non Stop flag to mark that this station
2187 * is the actual destination of the vehicle, which is (for example)
2188 * necessary to be known for HandleTrainLoading to determine
2189 * whether the train is lost or not; not marking a train lost
2190 * that arrives at random stations is bad. */
2191 this->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
2192
2193 } else {
2194 /* We weren't scheduled to stop here. Insert an implicit order
2195 * to show that we are stopping here.
2196 * While only groundvehicles have implicit orders, e.g. aircraft might still enter
2197 * the 'wrong' terminal when skipping orders etc. */
2198 Order *in_list = this->GetOrder(this->cur_implicit_order_index);
2199 if (this->IsGroundVehicle() &&
2200 (in_list == nullptr || !in_list->IsType(OT_IMPLICIT) ||
2201 in_list->GetDestination() != this->last_station_visited)) {
2202 bool suppress_implicit_orders = HasBit(this->GetGroundVehicleFlags(), GVF_SUPPRESS_IMPLICIT_ORDERS);
2203 /* Do not create consecutive duplicates of implicit orders */
2204 Order *prev_order = this->cur_implicit_order_index > 0 ? this->GetOrder(this->cur_implicit_order_index - 1) : (this->GetNumOrders() > 1 ? this->GetLastOrder() : nullptr);
2205 if (prev_order == nullptr ||
2206 (!prev_order->IsType(OT_IMPLICIT) && !prev_order->IsType(OT_GOTO_STATION)) ||
2207 prev_order->GetDestination() != this->last_station_visited) {
2208
2209 /* Prefer deleting implicit orders instead of inserting new ones,
2210 * so test whether the right order follows later. In case of only
2211 * implicit orders treat the last order in the list like an
2212 * explicit one, except if the overall number of orders surpasses
2213 * IMPLICIT_ORDER_ONLY_CAP. */
2214 int target_index = this->cur_implicit_order_index;
2215 bool found = false;
2216 while (target_index != this->cur_real_order_index || this->GetNumManualOrders() == 0) {
2217 const Order *order = this->GetOrder(target_index);
2218 if (order == nullptr) break; // No orders.
2219 if (order->IsType(OT_IMPLICIT) && order->GetDestination() == this->last_station_visited) {
2220 found = true;
2221 break;
2222 }
2223 target_index++;
2224 if (target_index >= this->orders->GetNumOrders()) {
2225 if (this->GetNumManualOrders() == 0 &&
2226 this->GetNumOrders() < IMPLICIT_ORDER_ONLY_CAP) {
2227 break;
2228 }
2229 target_index = 0;
2230 }
2231 if (target_index == this->cur_implicit_order_index) break; // Avoid infinite loop.
2232 }
2233
2234 if (found) {
2235 if (suppress_implicit_orders) {
2236 /* Skip to the found order */
2237 this->cur_implicit_order_index = target_index;
2238 InvalidateVehicleOrder(this, 0);
2239 } else {
2240 /* Delete all implicit orders up to the station we just reached */
2241 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2242 while (!order->IsType(OT_IMPLICIT) || order->GetDestination() != this->last_station_visited) {
2243 if (order->IsType(OT_IMPLICIT)) {
2244 DeleteOrder(this, this->cur_implicit_order_index);
2245 /* DeleteOrder does various magic with order_indices, so resync 'order' with 'cur_implicit_order_index' */
2246 order = this->GetOrder(this->cur_implicit_order_index);
2247 } else {
2248 /* Skip non-implicit orders, e.g. service-orders */
2249 order = order->next;
2250 this->cur_implicit_order_index++;
2251 }
2252
2253 /* Wrap around */
2254 if (order == nullptr) {
2255 order = this->GetOrder(0);
2256 this->cur_implicit_order_index = 0;
2257 }
2258 assert(order != nullptr);
2259 }
2260 }
2261 } else if (!suppress_implicit_orders &&
2262 ((this->orders == nullptr ? OrderList::CanAllocateItem() : this->orders->GetNumOrders() < MAX_VEH_ORDER_ID)) &&
2264 /* Insert new implicit order */
2265 Order *implicit_order = new Order();
2266 implicit_order->MakeImplicit(this->last_station_visited);
2267 InsertOrder(this, implicit_order, this->cur_implicit_order_index);
2268 if (this->cur_implicit_order_index > 0) --this->cur_implicit_order_index;
2269
2270 /* InsertOrder disabled creation of implicit orders for all vehicles with the same implicit order.
2271 * Reenable it for this vehicle */
2272 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2274 }
2275 }
2276 }
2277 this->current_order.MakeLoading(false);
2278 }
2279
2280 if (this->last_loading_station != StationID::Invalid() &&
2281 this->last_loading_station != this->last_station_visited &&
2282 ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2283 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
2284 IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited, travel_time);
2285 }
2286
2287 PrepareUnload(this);
2288
2289 SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
2291 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
2292 SetWindowDirty(WC_STATION_VIEW, this->last_station_visited);
2293
2294 Station::Get(this->last_station_visited)->MarkTilesDirty(true);
2295 this->cur_speed = 0;
2296 this->MarkDirty();
2297}
2298
2305{
2306 for (Vehicle *v = this; v != nullptr; v = v->next) {
2307 VehicleCargoList &cargo = v->cargo;
2308 if (cargo.ActionCount(VehicleCargoList::MTA_LOAD) > 0) {
2309 Debug(misc, 1, "cancelling cargo reservation");
2310 cargo.Return(UINT_MAX, &st->goods[v->cargo_type].GetOrCreateData().cargo, next, v->tile);
2311 }
2312 cargo.KeepAll();
2313 }
2314}
2315
2321{
2322 assert(this->current_order.IsType(OT_LOADING));
2323
2324 delete this->cargo_payment;
2325 assert(this->cargo_payment == nullptr); // cleared by ~CargoPayment
2326
2327 /* Only update the timetable if the vehicle was supposed to stop here. */
2328 if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
2329
2330 if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
2331 (this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
2332 if (this->current_order.CanLeaveWithCargo(this->last_loading_station != StationID::Invalid())) {
2333 /* Refresh next hop stats to make sure we've done that at least once
2334 * during the stop and that refit_cap == cargo_cap for each vehicle in
2335 * the consist. */
2336 this->ResetRefitCaps();
2337 LinkRefresher::Run(this);
2338
2339 /* if the vehicle could load here or could stop with cargo loaded set the last loading station */
2340 this->last_loading_station = this->last_station_visited;
2341 this->last_loading_tick = TimerGameTick::counter;
2342 } else {
2343 /* if the vehicle couldn't load and had to unload or transfer everything
2344 * set the last loading station to invalid as it will leave empty. */
2345 this->last_loading_station = StationID::Invalid();
2346 }
2347 }
2348
2349 this->current_order.MakeLeaveStation();
2350 Station *st = Station::Get(this->last_station_visited);
2351 this->CancelReservation(StationID::Invalid(), st);
2352 st->loading_vehicles.remove(this);
2353
2354 HideFillingPercent(&this->fill_percent_te_id);
2355 trip_occupancy = CalcPercentVehicleFilled(this, nullptr);
2356
2357 if (this->type == VEH_TRAIN && !this->vehstatus.Test(VehState::Crashed)) {
2358 /* Trigger station animation (trains only) */
2359 if (IsTileType(this->tile, MP_STATION)) {
2361 TriggerStationAnimation(st, this->tile, StationAnimationTrigger::VehicleDeparts);
2362 }
2363
2364 SetBit(Train::From(this)->flags, VRF_LEAVING_STATION);
2365 }
2366 if (this->type == VEH_ROAD && !this->vehstatus.Test(VehState::Crashed)) {
2367 /* Trigger road stop animation */
2368 if (IsStationRoadStopTile(this->tile)) {
2369 TriggerRoadStopRandomisation(st, this->tile, StationRandomTrigger::VehicleDeparts);
2370 TriggerRoadStopAnimation(st, this->tile, StationAnimationTrigger::VehicleDeparts);
2371 }
2372 }
2373
2374
2375 this->MarkDirty();
2376}
2377
2382{
2383 for (Vehicle *v = this; v != nullptr; v = v->Next()) v->refit_cap = v->cargo_cap;
2384}
2385
2390{
2391 Company::Get(this->owner)->freeunits[this->type].ReleaseID(this->unitnumber);
2392 this->unitnumber = 0;
2393}
2394
2401{
2402 switch (this->current_order.GetType()) {
2403 case OT_LOADING: {
2404 TimerGameTick::Ticks wait_time = std::max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0);
2405
2406 /* Not the first call for this tick, or still loading */
2407 if (mode || !this->vehicle_flags.Test(VehicleFlag::LoadingFinished) || this->current_order_time < wait_time) return;
2408
2409 this->PlayLeaveStationSound();
2410
2411 this->LeaveStation();
2412
2413 /* Only advance to next order if we just loaded at the current one */
2414 const Order *order = this->GetOrder(this->cur_implicit_order_index);
2415 if (order == nullptr ||
2416 (!order->IsType(OT_IMPLICIT) && !order->IsType(OT_GOTO_STATION)) ||
2417 order->GetDestination() != this->last_station_visited) {
2418 return;
2419 }
2420 break;
2421 }
2422
2423 case OT_DUMMY: break;
2424
2425 default: return;
2426 }
2427
2428 this->IncrementImplicitOrderIndex();
2429}
2430
2436{
2437 for (Order *o : this->Orders()) {
2438 if (o->IsType(OT_GOTO_STATION) && o->GetLoadType() & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY)) return true;
2439 }
2440 return false;
2441}
2442
2448{
2449 for (Order *o : this->Orders()) {
2450 if (o->IsType(OT_CONDITIONAL)) return true;
2451 }
2452 return false;
2453}
2454
2460{
2461 for (Order *o : this->Orders()) {
2462 if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotActionType() & ODATFB_UNBUNCH) return true;
2463 }
2464 return false;
2465}
2466
2472{
2473 /* If we are headed for the first order, we must wrap around back to the last order. */
2474 bool is_first_order = (v->GetOrder(v->cur_implicit_order_index) == v->GetFirstOrder());
2475 Order *previous_order = (is_first_order) ? v->GetLastOrder() : v->GetOrder(v->cur_implicit_order_index - 1);
2476
2477 if (previous_order == nullptr || !previous_order->IsType(OT_GOTO_DEPOT)) return false;
2478 return (previous_order->GetDepotActionType() & ODATFB_UNBUNCH) != 0;
2479}
2480
2485{
2486 /* Don't do anything if this is not our unbunching order. */
2487 if (!PreviousOrderIsUnbunching(this)) return;
2488
2489 /* Set the start point for this round trip time. */
2490 this->depot_unbunching_last_departure = TimerGameTick::counter;
2491
2492 /* Tell the timetable we are now "on time." */
2493 this->lateness_counter = 0;
2495
2496 /* Find the average travel time of vehicles that we share orders with. */
2497 int num_vehicles = 0;
2498 TimerGameTick::Ticks total_travel_time = 0;
2499
2500 Vehicle *u = this->FirstShared();
2501 for (; u != nullptr; u = u->NextShared()) {
2502 /* Ignore vehicles that are manually stopped or crashed. */
2503 if (u->vehstatus.Any({VehState::Stopped, VehState::Crashed})) continue;
2504
2505 num_vehicles++;
2506 total_travel_time += u->round_trip_time;
2507 }
2508
2509 /* Make sure we cannot divide by 0. */
2510 num_vehicles = std::max(num_vehicles, 1);
2511
2512 /* Calculate the separation by finding the average travel time, then calculating equal separation (minimum 1 tick) between vehicles. */
2513 TimerGameTick::Ticks separation = std::max((total_travel_time / num_vehicles / num_vehicles), 1);
2514 TimerGameTick::TickCounter next_departure = TimerGameTick::counter + separation;
2515
2516 /* Set the departure time of all vehicles that we share orders with. */
2517 u = this->FirstShared();
2518 for (; u != nullptr; u = u->NextShared()) {
2519 /* Ignore vehicles that are manually stopped or crashed. */
2520 if (u->vehstatus.Any({VehState::Stopped, VehState::Crashed})) continue;
2521
2522 u->depot_unbunching_next_departure = next_departure;
2524 }
2525}
2526
2532{
2533 assert(this->IsInDepot());
2534
2535 /* Don't bother if there are no vehicles sharing orders. */
2536 if (!this->IsOrderListShared()) return false;
2537
2538 /* Don't do anything if there aren't enough orders. */
2539 if (this->GetNumOrders() <= 1) return false;
2540
2541 /* Don't do anything if this is not our unbunching order. */
2542 if (!PreviousOrderIsUnbunching(this)) return false;
2543
2544 return (this->depot_unbunching_next_departure > TimerGameTick::counter);
2545};
2546
2554{
2555 CommandCost ret = CheckOwnership(this->owner);
2556 if (ret.Failed()) return ret;
2557
2558 if (this->vehstatus.Test(VehState::Crashed)) return CMD_ERROR;
2559 if (this->IsStoppedInDepot()) return CMD_ERROR;
2560
2561 /* No matter why we're headed to the depot, unbunching data is no longer valid. */
2562 if (flags.Test(DoCommandFlag::Execute)) this->ResetDepotUnbunching();
2563
2564 if (this->current_order.IsType(OT_GOTO_DEPOT)) {
2565 bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
2566 if (command.Test(DepotCommandFlag::Service) == halt_in_depot) {
2567 /* We called with a different DEPOT_SERVICE setting.
2568 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
2569 * Note: the if is (true for requesting service == true for ordered to stop in depot) */
2570 if (flags.Test(DoCommandFlag::Execute)) {
2571 this->current_order.SetDepotOrderType(ODTF_MANUAL);
2572 this->current_order.SetDepotActionType(halt_in_depot ? ODATF_SERVICE_ONLY : ODATFB_HALT);
2574 }
2575 return CommandCost();
2576 }
2577
2578 if (command.Test(DepotCommandFlag::DontCancel)) return CMD_ERROR; // Requested no cancellation of depot orders
2579 if (flags.Test(DoCommandFlag::Execute)) {
2580 /* If the orders to 'goto depot' are in the orders list (forced servicing),
2581 * then skip to the next order; effectively cancelling this forced service */
2582 if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
2583
2584 if (this->IsGroundVehicle()) {
2585 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2587 }
2588
2589 this->current_order.MakeDummy();
2591 }
2592 return CommandCost();
2593 }
2594
2595 ClosestDepot closest_depot = this->FindClosestDepot();
2596 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};
2597 if (!closest_depot.found) return CommandCost(no_depot[this->type]);
2598
2599 if (flags.Test(DoCommandFlag::Execute)) {
2600 if (this->current_order.IsType(OT_LOADING)) this->LeaveStation();
2601
2602 if (this->IsGroundVehicle() && this->GetNumManualOrders() > 0) {
2603 uint16_t &gv_flags = this->GetGroundVehicleFlags();
2605 }
2606
2607 this->SetDestTile(closest_depot.location);
2608 this->current_order.MakeGoToDepot(closest_depot.destination.ToDepotID(), ODTF_MANUAL);
2609 if (!command.Test(DepotCommandFlag::Service)) this->current_order.SetDepotActionType(ODATFB_HALT);
2611
2612 /* If there is no depot in front and the train is not already reversing, reverse automatically (trains only) */
2613 if (this->type == VEH_TRAIN && (closest_depot.reverse ^ HasBit(Train::From(this)->flags, VRF_REVERSING))) {
2615 }
2616
2617 if (this->type == VEH_AIRCRAFT) {
2618 Aircraft *a = Aircraft::From(this);
2619 if (a->state == FLYING && a->targetairport != closest_depot.destination) {
2620 /* The aircraft is now heading for a different hangar than the next in the orders */
2622 }
2623 }
2624 }
2625
2626 return CommandCost();
2627
2628}
2629
2634void Vehicle::UpdateVisualEffect(bool allow_power_change)
2635{
2636 bool powered_before = HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2637 const Engine *e = this->GetEngine();
2638
2639 /* Evaluate properties */
2640 uint8_t visual_effect;
2641 switch (e->type) {
2642 case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break;
2643 case VEH_ROAD: visual_effect = e->u.road.visual_effect; break;
2644 case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break;
2645 default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
2646 }
2647
2648 /* Check powered wagon / visual effect callback */
2650 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_VISUAL_EFFECT, 0, 0, this->engine_type, this);
2651
2652 if (callback != CALLBACK_FAILED) {
2653 if (callback >= 0x100 && e->GetGRF()->grf_version >= 8) ErrorUnknownCallbackResult(e->GetGRFID(), CBID_VEHICLE_VISUAL_EFFECT, callback);
2654
2655 callback = GB(callback, 0, 8);
2656 /* Avoid accidentally setting 'visual_effect' to the default value
2657 * Since bit 6 (disable effects) is set anyways, we can safely erase some bits. */
2658 if (callback == VE_DEFAULT) {
2659 assert(HasBit(callback, VE_DISABLE_EFFECT));
2660 SB(callback, VE_TYPE_START, VE_TYPE_COUNT, 0);
2661 }
2662 visual_effect = callback;
2663 }
2664 }
2665
2666 /* Apply default values */
2667 if (visual_effect == VE_DEFAULT ||
2668 (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
2669 /* Only train engines have default effects.
2670 * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
2671 if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) {
2672 if (visual_effect == VE_DEFAULT) {
2673 visual_effect = 1 << VE_DISABLE_EFFECT;
2674 } else {
2675 SetBit(visual_effect, VE_DISABLE_EFFECT);
2676 }
2677 } else {
2678 if (visual_effect == VE_DEFAULT) {
2679 /* Also set the offset */
2680 visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
2681 }
2682 SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM);
2683 }
2684 }
2685
2686 this->vcache.cached_vis_effect = visual_effect;
2687
2688 if (!allow_power_change && powered_before != HasBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER)) {
2689 ToggleBit(this->vcache.cached_vis_effect, VE_DISABLE_WAGON_POWER);
2690 ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GRFBug::VehPoweredWagon, false);
2691 }
2692}
2693
2694static const int8_t _vehicle_smoke_pos[8] = {
2695 1, 1, 1, 0, -1, -1, -1, 0
2696};
2697
2703{
2704 std::array<int32_t, 4> regs100;
2705 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_SPAWN_VISUAL_EFFECT, 0, Random(), v->engine_type, v, regs100);
2706 if (callback == CALLBACK_FAILED) return;
2707
2708 uint count = GB(callback, 0, 2);
2709 assert(count <= std::size(regs100));
2710 bool auto_center = HasBit(callback, 13);
2711 bool auto_rotate = !HasBit(callback, 14);
2712
2713 int8_t l_center = 0;
2714 if (auto_center) {
2715 /* For road vehicles: Compute offset from vehicle position to vehicle center */
2716 if (v->type == VEH_ROAD) l_center = -(int)(VEHICLE_LENGTH - RoadVehicle::From(v)->gcache.cached_veh_length) / 2;
2717 } else {
2718 /* For trains: Compute offset from vehicle position to sprite position */
2719 if (v->type == VEH_TRAIN) l_center = (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2720 }
2721
2722 Direction l_dir = v->direction;
2723 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) l_dir = ReverseDir(l_dir);
2724 Direction t_dir = ChangeDir(l_dir, DIRDIFF_90RIGHT);
2725
2726 int8_t x_center = _vehicle_smoke_pos[l_dir] * l_center;
2727 int8_t y_center = _vehicle_smoke_pos[t_dir] * l_center;
2728
2729 for (uint i = 0; i < count; i++) {
2730 int32_t reg = regs100[i];
2731 uint type = GB(reg, 0, 8);
2732 int8_t x = GB(reg, 8, 8);
2733 int8_t y = GB(reg, 16, 8);
2734 int8_t z = GB(reg, 24, 8);
2735
2736 if (auto_rotate) {
2737 int8_t l = x;
2738 int8_t t = y;
2739 x = _vehicle_smoke_pos[l_dir] * l + _vehicle_smoke_pos[t_dir] * t;
2740 y = _vehicle_smoke_pos[t_dir] * l - _vehicle_smoke_pos[l_dir] * t;
2741 }
2742
2743 if (type >= 0xF0) {
2744 switch (type) {
2745 case 0xF1: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_STEAM_SMOKE); break;
2746 case 0xF2: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_DIESEL_SMOKE); break;
2747 case 0xF3: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_ELECTRIC_SPARK); break;
2748 case 0xFA: CreateEffectVehicleRel(v, x_center + x, y_center + y, z, EV_BREAKDOWN_SMOKE_AIRCRAFT); break;
2749 default: break;
2750 }
2751 }
2752 }
2753}
2754
2760{
2761 assert(this->IsPrimaryVehicle());
2762 bool sound = false;
2763
2764 /* Do not show any smoke when:
2765 * - vehicle smoke is disabled by the player
2766 * - the vehicle is slowing down or stopped (by the player)
2767 * - the vehicle is moving very slowly
2768 */
2770 this->vehstatus.Any({VehState::TrainSlowing, VehState::Stopped}) ||
2771 this->cur_speed < 2) {
2772 return;
2773 }
2774
2775 /* Use the speed as limited by underground and orders. */
2776 uint max_speed = this->GetCurrentMaxSpeed();
2777
2778 if (this->type == VEH_TRAIN) {
2779 const Train *t = Train::From(this);
2780 /* For trains, do not show any smoke when:
2781 * - the train is reversing
2782 * - is entering a station with an order to stop there and its speed is equal to maximum station entering speed
2783 */
2784 if (HasBit(t->flags, VRF_REVERSING) ||
2786 t->cur_speed >= max_speed)) {
2787 return;
2788 }
2789 }
2790
2791 const Vehicle *v = this;
2792
2793 do {
2796 VisualEffectSpawnModel effect_model = VESM_NONE;
2797 if (advanced) {
2798 effect_offset = VE_OFFSET_CENTRE;
2800 if (effect_model >= VESM_END) effect_model = VESM_NONE; // unknown spawning model
2801 } else {
2803 assert(effect_model != (VisualEffectSpawnModel)VE_TYPE_DEFAULT); // should have been resolved by UpdateVisualEffect
2804 static_assert((uint)VESM_STEAM == (uint)VE_TYPE_STEAM);
2805 static_assert((uint)VESM_DIESEL == (uint)VE_TYPE_DIESEL);
2806 static_assert((uint)VESM_ELECTRIC == (uint)VE_TYPE_ELECTRIC);
2807 }
2808
2809 /* Show no smoke when:
2810 * - Smoke has been disabled for this vehicle
2811 * - The vehicle is not visible
2812 * - The vehicle is under a bridge
2813 * - The vehicle is on a depot tile
2814 * - The vehicle is on a tunnel tile
2815 * - The vehicle is a train engine that is currently unpowered */
2816 if (effect_model == VESM_NONE ||
2818 IsBridgeAbove(v->tile) ||
2819 IsDepotTile(v->tile) ||
2820 IsTunnelTile(v->tile) ||
2821 (v->type == VEH_TRAIN &&
2822 !HasPowerOnRail(Train::From(v)->railtype, GetTileRailType(v->tile)))) {
2823 continue;
2824 }
2825
2826 EffectVehicleType evt = EV_END;
2827 switch (effect_model) {
2828 case VESM_STEAM:
2829 /* Steam smoke - amount is gradually falling until vehicle reaches its maximum speed, after that it's normal.
2830 * Details: while vehicle's current speed is gradually increasing, steam plumes' density decreases by one third each
2831 * third of its maximum speed spectrum. Steam emission finally normalises at very close to vehicle's maximum speed.
2832 * REGULATION:
2833 * - instead of 1, 4 / 2^smoke_amount (max. 2) is used to provide sufficient regulation to steam puffs' amount. */
2834 if (GB(v->tick_counter, 0, ((4 >> _settings_game.vehicle.smoke_amount) + ((this->cur_speed * 3) / max_speed))) == 0) {
2835 evt = EV_STEAM_SMOKE;
2836 }
2837 break;
2838
2839 case VESM_DIESEL: {
2840 /* Diesel smoke - thicker when vehicle is starting, gradually subsiding till it reaches its maximum speed
2841 * when smoke emission stops.
2842 * Details: Vehicle's (max.) speed spectrum is divided into 32 parts. When max. speed is reached, chance for smoke
2843 * emission erodes by 32 (1/4). For trains, power and weight come in handy too to either increase smoke emission in
2844 * 6 steps (1000HP each) if the power is low or decrease smoke emission in 6 steps (512 tonnes each) if the train
2845 * isn't overweight. Power and weight contributions are expressed in a way that neither extreme power, nor
2846 * extreme weight can ruin the balance (e.g. FreightWagonMultiplier) in the formula. When the vehicle reaches
2847 * maximum speed no diesel_smoke is emitted.
2848 * REGULATION:
2849 * - up to which speed a diesel vehicle is emitting smoke (with reduced/small setting only until 1/2 of max_speed),
2850 * - in Chance16 - the last value is 512 / 2^smoke_amount (max. smoke when 128 = smoke_amount of 2). */
2851 int power_weight_effect = 0;
2852 if (v->type == VEH_TRAIN) {
2853 power_weight_effect = (32 >> (Train::From(this)->gcache.cached_power >> 10)) - (32 >> (Train::From(this)->gcache.cached_weight >> 9));
2854 }
2855 if (this->cur_speed < (max_speed >> (2 >> _settings_game.vehicle.smoke_amount)) &&
2856 Chance16((64 - ((this->cur_speed << 5) / max_speed) + power_weight_effect), (512 >> _settings_game.vehicle.smoke_amount))) {
2857 evt = EV_DIESEL_SMOKE;
2858 }
2859 break;
2860 }
2861
2862 case VESM_ELECTRIC:
2863 /* Electric train's spark - more often occurs when train is departing (more load)
2864 * Details: Electric locomotives are usually at least twice as powerful as their diesel counterparts, so spark
2865 * emissions are kept simple. Only when starting, creating huge force are sparks more likely to happen, but when
2866 * reaching its max. speed, quarter by quarter of it, chance decreases until the usual 2,22% at train's top speed.
2867 * REGULATION:
2868 * - in Chance16 the last value is 360 / 2^smoke_amount (max. sparks when 90 = smoke_amount of 2). */
2869 if (GB(v->tick_counter, 0, 2) == 0 &&
2870 Chance16((6 - ((this->cur_speed << 2) / max_speed)), (360 >> _settings_game.vehicle.smoke_amount))) {
2871 evt = EV_ELECTRIC_SPARK;
2872 }
2873 break;
2874
2875 default:
2876 NOT_REACHED();
2877 }
2878
2879 if (evt != EV_END && advanced) {
2880 sound = true;
2882 } else if (evt != EV_END) {
2883 sound = true;
2884
2885 /* The effect offset is relative to a point 4 units behind the vehicle's
2886 * front (which is the center of an 8/8 vehicle). Shorter vehicles need a
2887 * correction factor. */
2888 if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
2889
2890 int x = _vehicle_smoke_pos[v->direction] * effect_offset;
2891 int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;
2892
2893 if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION)) {
2894 x = -x;
2895 y = -y;
2896 }
2897
2898 CreateEffectVehicleRel(v, x, y, 10, evt);
2899 }
2900 } while ((v = v->Next()) != nullptr);
2901
2902 if (sound) PlayVehicleSound(this, VSE_VISUAL_EFFECT);
2903}
2904
2910{
2911 assert(this != next);
2912
2913 if (this->next != nullptr) {
2914 /* We had an old next vehicle. Update the first and previous pointers */
2915 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2916 v->first = this->next;
2917 }
2918 this->next->previous = nullptr;
2919 }
2920
2921 this->next = next;
2922
2923 if (this->next != nullptr) {
2924 /* A new next vehicle. Update the first and previous pointers */
2925 if (this->next->previous != nullptr) this->next->previous->next = nullptr;
2926 this->next->previous = this;
2927 for (Vehicle *v = this->next; v != nullptr; v = v->Next()) {
2928 v->first = this->first;
2929 }
2930 }
2931}
2932
2939{
2940 assert(this->previous_shared == nullptr && this->next_shared == nullptr);
2941
2942 if (shared_chain->orders == nullptr) {
2943 assert(shared_chain->previous_shared == nullptr);
2944 assert(shared_chain->next_shared == nullptr);
2945 this->orders = shared_chain->orders = new OrderList(nullptr, shared_chain);
2946 }
2947
2948 this->next_shared = shared_chain->next_shared;
2949 this->previous_shared = shared_chain;
2950
2951 shared_chain->next_shared = this;
2952
2953 if (this->next_shared != nullptr) this->next_shared->previous_shared = this;
2954
2955 shared_chain->orders->AddVehicle(this);
2956}
2957
2962{
2963 /* Remember if we were first and the old window number before RemoveVehicle()
2964 * as this changes first if needed. */
2965 bool were_first = (this->FirstShared() == this);
2966 VehicleListIdentifier vli(VL_SHARED_ORDERS, this->type, this->owner, this->FirstShared()->index);
2967
2968 this->orders->RemoveVehicle(this);
2969
2970 if (!were_first) {
2971 /* We are not the first shared one, so only relink our previous one. */
2972 this->previous_shared->next_shared = this->NextShared();
2973 }
2974
2975 if (this->next_shared != nullptr) this->next_shared->previous_shared = this->previous_shared;
2976
2977
2978 if (this->orders->GetNumVehicles() == 1) {
2979 /* When there is only one vehicle, remove the shared order list window. */
2981 InvalidateVehicleOrder(this->FirstShared(), VIWD_MODIFY_ORDERS);
2982 } else if (were_first) {
2983 /* If we were the first one, update to the new first one.
2984 * Note: FirstShared() is already the new first */
2985 InvalidateWindowData(GetWindowClassForVehicleType(this->type), vli.ToWindowNumber(), this->FirstShared()->index.base() | (1U << 31));
2986 }
2987
2988 this->next_shared = nullptr;
2989 this->previous_shared = nullptr;
2990}
2991
2992static const IntervalTimer<TimerGameEconomy> _economy_vehicles_yearly({TimerGameEconomy::YEAR, TimerGameEconomy::Priority::VEHICLE}, [](auto)
2993{
2994 for (Vehicle *v : Vehicle::Iterate()) {
2995 if (v->IsPrimaryVehicle()) {
2996 /* show warning if vehicle is not generating enough income last 2 years (corresponds to a red icon in the vehicle list) */
2997 Money profit = v->GetDisplayProfitThisYear();
2998 if (v->economy_age >= VEHICLE_PROFIT_MIN_AGE && profit < 0) {
3001 GetEncodedString(TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD : STR_NEWS_VEHICLE_UNPROFITABLE_YEAR, v->index, profit),
3002 v->index);
3003 }
3004 AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index));
3005 }
3006
3008 v->profit_this_year = 0;
3010 }
3011 }
3017});
3018
3028bool CanVehicleUseStation(EngineID engine_type, const Station *st)
3029{
3030 const Engine *e = Engine::GetIfValid(engine_type);
3031 assert(e != nullptr);
3032
3033 switch (e->type) {
3034 case VEH_TRAIN:
3036
3037 case VEH_ROAD:
3038 /* For road vehicles we need the vehicle to know whether it can actually
3039 * use the station, but if it doesn't have facilities for RVs it is
3040 * certainly not possible that the station can be used. */
3042
3043 case VEH_SHIP:
3045
3046 case VEH_AIRCRAFT:
3049
3050 default:
3051 return false;
3052 }
3053}
3054
3061bool CanVehicleUseStation(const Vehicle *v, const Station *st)
3062{
3063 if (v->type == VEH_ROAD) return st->GetPrimaryRoadStop(RoadVehicle::From(v)) != nullptr;
3064
3065 return CanVehicleUseStation(v->engine_type, st);
3066}
3067
3075{
3076 switch (v->type) {
3077 case VEH_TRAIN:
3078 return STR_ERROR_NO_RAIL_STATION;
3079
3080 case VEH_ROAD: {
3081 const RoadVehicle *rv = RoadVehicle::From(v);
3082 RoadStop *rs = st->GetPrimaryRoadStop(rv->IsBus() ? RoadStopType::Bus : RoadStopType::Truck);
3083
3084 StringID err = rv->IsBus() ? STR_ERROR_NO_BUS_STATION : STR_ERROR_NO_TRUCK_STATION;
3085
3086 for (; rs != nullptr; rs = rs->next) {
3087 /* Articulated vehicles cannot use bay road stops, only drive-through. Make sure the vehicle can actually use this bay stop */
3089 err = STR_ERROR_NO_STOP_ARTICULATED_VEHICLE;
3090 continue;
3091 }
3092
3093 /* Bay stop errors take precedence, but otherwise the vehicle may not be compatible with the roadtype/tramtype of this station tile.
3094 * We give bay stop errors precedence because they are usually a bus sent to a tram station or vice versa. */
3095 if (!HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && err != STR_ERROR_NO_STOP_ARTICULATED_VEHICLE) {
3096 err = RoadTypeIsRoad(rv->roadtype) ? STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE : STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE;
3097 continue;
3098 }
3099 }
3100
3101 return err;
3102 }
3103
3104 case VEH_SHIP:
3105 return STR_ERROR_NO_DOCK;
3106
3107 case VEH_AIRCRAFT:
3108 if (!st->facilities.Test(StationFacility::Airport)) return STR_ERROR_NO_AIRPORT;
3109 if (v->GetEngine()->u.air.subtype & AIR_CTOL) {
3110 return STR_ERROR_AIRPORT_NO_PLANES;
3111 } else {
3112 return STR_ERROR_AIRPORT_NO_HELICOPTERS;
3113 }
3114
3115 default:
3116 return INVALID_STRING_ID;
3117 }
3118}
3119
3126{
3127 assert(this->IsGroundVehicle());
3128 if (this->type == VEH_TRAIN) {
3129 return &Train::From(this)->gcache;
3130 } else {
3131 return &RoadVehicle::From(this)->gcache;
3132 }
3133}
3134
3141{
3142 assert(this->IsGroundVehicle());
3143 if (this->type == VEH_TRAIN) {
3144 return &Train::From(this)->gcache;
3145 } else {
3146 return &RoadVehicle::From(this)->gcache;
3147 }
3148}
3149
3156{
3157 assert(this->IsGroundVehicle());
3158 if (this->type == VEH_TRAIN) {
3159 return Train::From(this)->gv_flags;
3160 } else {
3161 return RoadVehicle::From(this)->gv_flags;
3162 }
3163}
3164
3170const uint16_t &Vehicle::GetGroundVehicleFlags() const
3171{
3172 assert(this->IsGroundVehicle());
3173 if (this->type == VEH_TRAIN) {
3174 return Train::From(this)->gv_flags;
3175 } else {
3176 return RoadVehicle::From(this)->gv_flags;
3177 }
3178}
3179
3188void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8_t num_vehicles)
3189{
3190 if (v->type == VEH_TRAIN) {
3191 Train *u = Train::From(v);
3192 /* Only include whole vehicles, so start with the first articulated part */
3193 u = u->GetFirstEnginePart();
3194
3195 /* Include num_vehicles vehicles, not counting articulated parts */
3196 for (; u != nullptr && num_vehicles > 0; num_vehicles--) {
3197 do {
3198 /* Include current vehicle in the selection. */
3199 include(set, u->index);
3200
3201 /* If the vehicle is multiheaded, add the other part too. */
3202 if (u->IsMultiheaded()) include(set, u->other_multiheaded_part->index);
3203
3204 u = u->Next();
3205 } while (u != nullptr && u->IsArticulatedPart());
3206 }
3207 }
3208}
3209
3215{
3216 uint32_t max_weight = 0;
3217
3218 for (const Vehicle *u = this; u != nullptr; u = u->Next()) {
3219 max_weight += u->GetMaxWeight();
3220 }
3221
3222 return max_weight;
3223}
3224
3230{
3231 uint32_t max_weight = GetDisplayMaxWeight();
3232 if (max_weight == 0) return 0;
3233 return GetGroundVehicleCache()->cached_power * 10u / max_weight;
3234}
3235
3243{
3244 while (true) {
3245 if (v1 == nullptr && v2 == nullptr) return true;
3246 if (v1 == nullptr || v2 == nullptr) return false;
3247 if (v1->GetEngine() != v2->GetEngine()) return false;
3248 v1 = v1->GetNextVehicle();
3249 v2 = v2->GetNextVehicle();
3250 }
3251}
3252
3260{
3261 const Order *o1 = v1->GetFirstOrder();
3262 const Order *o2 = v2->GetFirstOrder();
3263 while (true) {
3264 if (o1 == nullptr && o2 == nullptr) return true;
3265 if (o1 == nullptr || o2 == nullptr) return false;
3266 if (!o1->Equals(*o2)) return false;
3267 o1 = o1->next;
3268 o2 = o2->next;
3269 }
3270}
Base functions for all AIs.
Base for aircraft.
void AircraftNextAirportPos_and_Order(Aircraft *v)
set the right pos when heading to other airports after takeoff
Station * GetTargetAirportIfValid(const Aircraft *v)
Returns aircraft's target station if v->target_airport is a valid station with airport.
@ AIR_SHADOW
shadow of the aircraft
Definition aircraft.h:31
void HandleAircraftEnterHangar(Aircraft *v)
Handle Aircraft specific tasks when an Aircraft enters a hangar.
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type)
Get cargo mask of all cargoes carried by an articulated vehicle.
void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask)
Merges the refit_masks of all articulated parts.
CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine)
Get the cargo mask of the parts of a given engine.
Functions related to articulated vehicles.
Command definitions related to autoreplace.
Functions related to autoreplacing.
EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group, bool *replace_when_old=nullptr)
Retrieve the engine replacement for the given company and original engine type.
bool EngineHasReplacementForCompany(const Company *c, EngineID engine, GroupID group)
Check if a company has a replacement set up for the given engine.
void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
Rebuild the left autoreplace list if an engine is removed or added.
Functions related to the autoreplace GUIs.
Class for backupping variables and making sure they are restored later.
@ PathfinderLost
Vehicle's pathfinder is lost.
@ StopLoading
Don't load anymore during the next load cycle.
@ LoadingFinished
Vehicle has finished loading.
@ CargoUnloading
Vehicle is unloading cargo.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr bool HasAtMostOneBit(T value)
Test whether value has at most 1 bit set.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Map accessor functions for bridges.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
@ Passengers
Passengers.
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
@ MTA_LOAD
Load the cargo from the station.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
StringID GetErrorMessage() const
Returns the error message of a command.
static constexpr int DAYS_IN_ECONOMY_MONTH
Days in an economy month, when in wallclock timekeeping mode.
Container for an encoded string, created by GetEncodedString.
Enum-as-bit-set wrapper.
UnitID UseID(UnitID index)
Use a unit number.
Definition vehicle.cpp:1833
void ReleaseID(UnitID index)
Release a unit number.
Definition vehicle.cpp:1850
UnitID NextID() const
Find first unused unit number.
Definition vehicle.cpp:1818
bool GRFBugReverse(uint32_t grfid, uint16_t internal_id)
Logs GRF bug - rail vehicle has different length after reversing.
Definition gamelog.cpp:482
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
static void Run(Vehicle *v, bool allow_merge=true, bool is_full_loading=false)
Refresh all links the given vehicle will visit.
Definition refresh.cpp:26
static void Reset(PerformanceElement elem)
Store the previous accumulator value and reset for a new cycle of accumulating measurements.
RAII class for measuring simple elements of performance.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static Date date
Current date in days (day counter).
static DateFract date_fract
Fractional part of the day.
static constexpr TimerGame< struct Economy >::Date MAX_DATE
The date of the last day of the max year.
static constexpr int DAYS_IN_LEAP_YEAR
sometimes, you need one day more...
static Date date
Current date in days (day counter).
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static DateFract date_fract
Fractional part of the day.
uint64_t TickCounter
The type that the tick counter is stored in.
static TickCounter counter
Monotonic counter, in ticks, since start of game.
int32_t Ticks
The type to store ticks in.
static constexpr Date DateAtStartOfYear(Year year)
Calculate the date of the first day of a given year.
CargoList that is used for vehicles.
uint ActionCount(MoveToAction action) const
Returns the amount of cargo designated for a given purpose.
uint Return(uint max_move, StationCargoList *dest, StationID next_station, TileIndex current_tile)
Returns reserved cargo to the station and removes it from the cache.
void KeepAll()
Marks all cargo in the vehicle as to be kept.
void AgeCargo()
Ages the all cargo in this list.
uint StoredCount() const
Returns sum of cargo on board the vehicle (ie not only reserved).
void SkipEmptyBuckets()
Advance the internal state until we reach a non-empty bucket, or the end.
Definition vehicle.cpp:472
Iterator(int32_t x, int32_t y, uint max_dist)
Iterator constructor.
Definition vehicle.cpp:432
void Increment()
Advance the internal state to the next potential vehicle.
Definition vehicle.cpp:462
void SkipFalseMatches()
Advance the internal state until it reaches a vehicle within the search area.
Definition vehicle.cpp:490
Iterator(TileIndex tile)
Iterator constructor.
Definition vehicle.cpp:499
void Increment()
Advance the internal state to the next potential vehicle.
Definition vehicle.cpp:509
void SkipFalseMatches()
Advance the internal state until it reaches a vehicle on the correct tile or the end.
Definition vehicle.cpp:517
Iterate over all vehicles on a tile.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
void SubtractMoneyFromCompany(const CommandCost &cost)
Subtract money from the _current_company, if the company is valid.
Money GetAvailableMoney(CompanyID company)
Get the amount of money that a company has available, or INT64_MAX if there is no such valid company.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
bool IsLocalCompany()
Is the current company the local company?
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Functions related to depots.
void DeleteDepotHighlightOfVehicle(const Vehicle *v)
Removes the highlight of a vehicle in a depot window.
Map related accessors for depots.
bool IsDepotTile(Tile tile)
Is the given tile a tile with a depot on it?
Definition depot_map.h:42
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DirDiff
Enumeration for the difference between two directions.
@ DIRDIFF_45LEFT
Angle of 45 degrees left.
@ DIRDIFF_REVERSE
One direction is the opposite of the other one.
@ DIRDIFF_45RIGHT
Angle of 45 degrees right.
@ DIRDIFF_SAME
Both directions faces to the same direction.
@ DIRDIFF_90RIGHT
Angle of 90 degrees right.
Direction
Defines the 8 directions on the map.
@ DIR_SW
Southwest.
@ DIR_NW
Northwest.
@ DIR_N
North.
@ DIR_SE
Southeast.
@ DIR_S
South.
@ DIR_NE
Northeast.
@ DIR_W
West.
@ DIR_E
East.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
void LoadUnloadStation(Station *st)
Load/unload the vehicles in this station according to the order they entered.
Definition economy.cpp:1933
void PrepareUnload(Vehicle *front_v)
Prepare the vehicle to be unloaded.
Definition economy.cpp:1261
Base classes related to the economy.
@ EXPENSES_NEW_VEHICLES
New vehicles.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
Base class for all effect vehicles.
Functions related to effect vehicles.
EffectVehicleType
Effect vehicle types.
@ EV_BREAKDOWN_SMOKE
Smoke of broken vehicles except aircraft.
@ EV_STEAM_SMOKE
Smoke of steam engines.
@ EV_DIESEL_SMOKE
Smoke of diesel engines.
@ EV_BREAKDOWN_SMOKE_AIRCRAFT
Smoke of broken aircraft.
@ EV_ELECTRIC_SPARK
Sparcs of electric engines.
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
@ RoadIsTram
Road vehicle is a tram/light rail vehicle.
@ NoBreakdownSmoke
Do not show black smoke during a breakdown.
@ Uses2CC
Vehicle uses two company colours.
@ RailIsMU
Rail vehicle is a multiple-unit (DMU/EMU)
@ EC_DIESEL
Diesel rail engine.
Definition engine_type.h:40
@ EC_STEAM
Steam rail engine.
Definition engine_type.h:39
@ EC_MAGLEV
Maglev engine.
Definition engine_type.h:43
@ EC_ELECTRIC
Electric rail engine.
Definition engine_type.h:41
@ EC_MONORAIL
Mono rail engine.
Definition engine_type.h:42
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
Functions related to errors.
@ WL_CRITICAL
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
Types for recording game performance data.
@ PFE_GL_SHIPS
Time spent processing ships.
@ PFE_GL_AIRCRAFT
Time spent processing aircraft.
@ PFE_GL_ECONOMY
Time spent processing cargo movement.
@ PFE_GL_ROADVEHS
Time spend processing road vehicles.
@ PFE_GL_TRAINS
Time spent processing trains.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1024
@ Normal
The most basic (normal) sprite.
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
@ GVF_SUPPRESS_IMPLICIT_ORDERS
Disable insertion and removal of automatic orders until the vehicle completes the real order.
bool MarkAllViewportsDirty(int left, int top, int right, int bottom)
Mark all viewports that display an area as dirty (in need of repaint).
void MarkTilesDirty(bool cargo_change) const
Marks the tiles of the station as dirty.
Definition station.cpp:247
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
virtual void MarkDirty()
Marks the vehicles to be redrawn and updates cached variables.
void DeleteGroupHighlightOfVehicle(const Vehicle *v)
Removes the highlight of a vehicle in a group window.
Functions/definitions that have something to do with groups.
static constexpr GroupID DEFAULT_GROUP
Ungrouped vehicles are in this group.
Definition group_type.h:18
const TileTypeProcs *const _tile_type_procs[16]
Tile callback functions for each type of tile.
Definition landscape.cpp:66
Point RemapCoords(int x, int y, int z)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap.
Definition landscape.h:79
@ Random
Randomise borders.
Declaration of link graph classes used for cargo distribution.
static const uint8_t LIT_ALL
Show the liveries of all companies.
Definition livery.h:18
LiveryScheme
List of different livery schemes.
Definition livery.h:21
static const uint8_t LIT_COMPANY
Show the liveries of your own company.
Definition livery.h:17
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:403
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Miscellaneous command definitions.
void HideFillingPercent(TextEffectID *te_id)
Hide vehicle loading indicators.
Definition misc_gui.cpp:581
void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
Display animated income or costs on the map.
Definition misc_gui.cpp:508
bool _networking
are we in networking mode?
Definition network.cpp:67
Basic functions/variables used all over the place.
@ VisualEffect
Visual effects and wagon power (trains, road vehicles and ships)
@ ColourRemap
Change colour mapping of vehicle.
@ CBID_VEHICLE_SPAWN_VISUAL_EFFECT
Called to spawn visual effects for vehicles.
@ CBID_VEHICLE_COLOUR_MAPPING
Called to determine if a specific colour map should be used for a vehicle instead of the default live...
@ CBID_VEHICLE_32DAY_CALLBACK
Called for every vehicle every 32 days (not all on same date though).
@ CBID_VEHICLE_VISUAL_EFFECT
Visual effects and wagon power.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res)
Record that a NewGRF returned an unknown/invalid callback result.
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
GRFBug
Encountered GRF bugs.
@ VehPoweredWagon
Powered wagon changed poweredness state when not inside a depot.
@ VehLength
Length of rail vehicle changes when not inside a depot.
Functions/types related to NewGRF debugging.
GrfSpecFeature GetGrfSpecFeature(TileIndex tile)
Get the GrfSpecFeature associated with the tile.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
bool UsesWagonOverride(const Vehicle *v)
Check if a wagon is currently using a wagon override.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
NewGRF definitions and structures for road stops.
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event, bool force)
Checks whether a NewGRF wants to play a different vehicle sound effect.
Functions related to NewGRF provided sounds.
@ VSE_VISUAL_EFFECT
Vehicle visual effect (steam, diesel smoke or electric spark) is shown.
@ VSE_RUNNING
Vehicle running normally.
@ VSE_STOPPED_16
Every 16 ticks while the vehicle is stopped (speed == 0).
@ VSE_RUNNING_16
Every 16 ticks while the vehicle is running (speed > 0).
@ VSE_BREAKDOWN
Vehicle breaking down.
void TriggerStationRandomisation(BaseStation *st, TileIndex trigger_tile, StationRandomTrigger trigger, CargoType cargo_type)
Trigger station randomisation.
Header file for NewGRF stations.
Functions related to news.
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:40
void DeleteVehicleNews(VehicleID vid, AdviceType advice_type=AdviceType::Invalid)
Delete news with a given advice type about a vehicle.
@ VehicleLost
The vehicle has become lost.
@ VehicleWaiting
The vehicle is waiting in the depot.
@ AutorenewFailed
Autorenew or autoreplace failed.
@ VehicleOld
The vehicle is starting to get old.
@ VehicleUnprofitable
The vehicle is costing you money.
@ RefitFailed
The refit order failed to execute.
@ Error
A game paused because a (critical) error.
@ Normal
A game normally paused.
Functions related to order backups.
void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
Insert a new order but skip the validation.
void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
Delete an order but skip the parameter validation.
void InvalidateVehicleOrder(const Vehicle *v, int data)
Updates the widgets of a vehicle which contains the order-data.
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indices)
Delete all orders from a vehicle.
@ OUFB_NO_UNLOAD
Totally no unloading will be done.
Definition order_type.h:72
@ ODTFB_PART_OF_ORDERS
This depot order is because of a regular order.
Definition order_type.h:112
@ ODTFB_SERVICE
This depot order is because of the servicing limit.
Definition order_type.h:111
@ ODTF_MANUAL
Manually initiated order.
Definition order_type.h:110
@ ODATFB_UNBUNCH
Service the vehicle and then unbunch it.
Definition order_type.h:122
@ ODATFB_NEAREST_DEPOT
Send the vehicle to the nearest depot.
Definition order_type.h:121
@ ODATFB_HALT
Service the vehicle and then halt it.
Definition order_type.h:120
@ ODATF_SERVICE_ONLY
Only service the vehicle.
Definition order_type.h:119
static const VehicleOrderID MAX_VEH_ORDER_ID
Last valid VehicleOrderID.
Definition order_type.h:42
@ OLFB_FULL_LOAD
Full load all cargoes of the consist.
Definition order_type.h:80
@ OLFB_NO_LOAD
Do not load anything.
Definition order_type.h:82
@ OLF_FULL_LOAD_ANY
Full load a single cargo of the consist.
Definition order_type.h:81
static const uint IMPLICIT_ORDER_ONLY_CAP
Maximum number of orders in implicit-only lists before we start searching harder for duplicates.
Definition order_type.h:48
@ ONSF_NO_STOP_AT_ANY_STATION
The vehicle will not stop at any stations it passes including the destination.
Definition order_type.h:92
@ ONSF_STOP_EVERYWHERE
The vehicle will stop at any station it passes and the destination.
Definition order_type.h:89
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
bool HasAnyRailTypesAvail(const CompanyID company)
Test if any buildable railtype is available for a company.
Definition rail.cpp:196
RailType GetTileRailType(Tile tile)
Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
Definition rail.cpp:155
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:341
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:269
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
bool Chance16I(const uint32_t a, const uint32_t b, const uint32_t r)
Checks if a given randomize-number is below a given probability.
bool Chance16(const uint32_t a, const uint32_t b, const std::source_location location=std::source_location::current())
Flips a coin with given probability.
Definition of link refreshing utility.
bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt)
Test if any buildable RoadType is available for a company.
Definition road.cpp:140
bool HasTileAnyRoadType(Tile t, RoadTypes rts)
Check if a tile has one of the specified road types.
Definition road_map.h:206
Base class for roadstops.
Road vehicle states.
@ RVSB_IN_DT_ROAD_STOP
The vehicle is in a drive-through road stop.
Definition roadveh.h:51
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:60
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:59
Base for ships.
SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner)
Update signals, starting at one side of a tile Will check tile next to this at opposite side too.
Definition signal.cpp:645
Functions related to sound.
@ SND_3A_BREAKDOWN_TRAIN_SHIP_TOYLAND
58 == 0x3A Breakdown: train or ship (toyland)
Definition sound_type.h:105
@ SND_10_BREAKDOWN_TRAIN_SHIP
14 == 0x0E Breakdown: train or ship (non-toyland)
Definition sound_type.h:61
@ SND_0F_BREAKDOWN_ROADVEHICLE
13 == 0x0D Breakdown: road vehicle (non-toyland)
Definition sound_type.h:60
@ SND_35_BREAKDOWN_ROADVEHICLE_TOYLAND
53 == 0x35 Breakdown: road vehicle (toyland)
Definition sound_type.h:100
Functions to cache sprites in memory.
static const PaletteID PALETTE_RECOLOUR_START
First recolour sprite for company colours.
Definition sprites.h:1578
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1609
Base classes/functions for stations.
void IncreaseStats(Station *st, CargoType cargo, StationID next_station_id, uint capacity, uint usage, uint32_t time, EdgeUpdateModes modes)
Increase capacity for a link stat given by station cargo and next hop.
bool IsStationRoadStopTile(Tile t)
Is tile t a road stop station?
bool IsBayRoadStopTile(Tile t)
Is tile t a bay (non-drive through) road stop station?
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
RoadStopType GetRoadStopType(Tile t)
Get the road stop type of this tile.
Definition station_map.h:56
@ Bus
A standard stop for buses.
@ Truck
A standard stop for trucks.
@ Dock
Station with a dock.
@ TruckStop
Station with truck stops.
@ Train
Station with train station.
@ Airport
Station with an airport.
@ BusStop
Station with bus stops.
@ VehicleDeparts
Trigger platform when train leaves.
@ VehicleDeparts
Trigger platform when train leaves.
Definition of base types and functions in a cross-platform compatible way.
static void StrMakeValid(Builder &builder, StringConsumer &consumer, StringValidationSettings settings)
Copies the valid (UTF-8) characters from consumer to the builder.
Definition string.cpp:117
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:91
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:415
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
uint8_t subtype
Type of aircraft.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
uint8_t pos
Next desired position of the aircraft.
Definition aircraft.h:74
uint8_t state
State of the airport.
Definition aircraft.h:77
bool IsNormalAircraft() const
Check if the aircraft type is a normal flying device; eg not a rotor or a shadow.
Definition aircraft.h:121
uint8_t previous_pos
Previous desired position of the aircraft.
Definition aircraft.h:75
StationID targetairport
Airport to go to next.
Definition aircraft.h:76
@ Airplanes
Can planes land on this airport type?
@ Helicopters
Can helicopters land on this airport type?
std::vector< AirportFTA > layout
state machine for airport
Definition airport.h:190
Flags flags
Flags for this airport type.
Definition airport.h:193
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
const AirportFTAClass * GetFTA() const
Get the finite-state machine for this airport or the finite-state machine for the dummy airport in ca...
Class to backup a specific variable and restore it later.
void Restore()
Restore the variable.
TimerGameTick::TickCounter depot_unbunching_next_departure
When the vehicle will next try to leave its unbunching depot.
VehicleOrderID cur_real_order_index
The index to the current real (non-implicit) order.
TimerGameTick::Ticks round_trip_time
How many ticks for a single circumnavigation of the orders.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
TimerGameTick::TickCounter depot_unbunching_last_departure
When the vehicle last left its unbunching depot.
VehicleOrderID cur_implicit_order_index
The index to the current implicit order.
void ResetDepotUnbunching()
Resets all the data used for depot unbunching.
StationFacilities facilities
The facilities that this station has.
VehicleType type
Type of vehicle.
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
GUISettings gui
settings related to the GUI
Structure to return information about the closest depot location, and whether it could be found.
DestinationID destination
The DestinationID as used for orders.
CompanySettings settings
settings specific for each company
uint32_t engine_renew_money
minimum amount of money before autorenew is used
int16_t engine_renew_months
months before/after the maximum vehicle age a vehicle should be renewed
bool engine_renew
is autorenew enabled
std::array< GroupStatistics, VEH_COMPANY_END > group_all
NOSAVE: Statistics for the ALL_GROUP group.
uint8_t vehicle_breakdowns
likelihood of vehicles breaking down
Data about how and where to blit pixels.
Definition gfx_type.h:158
A special vehicle is one of the following:
TransparencyOption GetTransparencyOption() const
Determines the transparency option affecting the effect.
uint16_t animation_state
State primarily used to change the graphics/behaviour.
EngineMiscFlags misc_flags
Miscellaneous flags.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:157
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
static Pool::IterateWrapperFiltered< Engine, EngineTypeFilter > IterateType(VehicleType vt, size_t from=0)
Returns an iterable ensemble of all valid engines of the given type.
CargoGRFFileProps grf_prop
Link to NewGRF.
Definition engine_base.h:77
CompanyMask company_avail
Bit for each company whether the engine is available for that company.
Definition engine_base.h:39
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:61
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:96
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:48
Information about GRF, used in the game and (part of it) in savegames.
GRFBugs grf_bugs
NOSAVE: bugs in this GRF in this run,.
std::string GetName() const
Get the name of this grf.
uint16_t local_id
id defined by the grf file for this entity
uint32_t grfid
grfid that introduced this entity.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:115
bool lost_vehicle_warn
if a vehicle can't find its destination, show a warning
bool vehicle_income_warn
if a vehicle isn't generating income, show a warning
bool show_track_reservation
highlight reserved tracks.
uint8_t liveries
options for displaying company liveries, 0=none, 1=self, 2=all
bool old_vehicle_warn
if a vehicle is getting old, show a warning
LandscapeType landscape
the landscape we're currently in
DifficultySettings difficulty
settings related to the difficulty
GameCreationSettings game_creation
settings used during the creation of a game (map)
VehicleSettings vehicle
options for vehicles
OrderSettings order
settings related to orders
Position information of a vehicle after it moved.
TileIndex new_tile
Tile of the vehicle after moving.
int y
x and y position of the vehicle after moving
TileIndex old_tile
Current tile of the vehicle.
Cached, frequently calculated values.
EngineID first_engine
Cached EngineID of the front vehicle. EngineID::Invalid() for the front vehicle itself.
bool IsMultiheaded() const
Check if the vehicle is a multiheaded engine.
static void CountVehicle(const Vehicle *v, int delta)
Update num_vehicle when adding or removing a vehicle.
static void VehicleReachedMinAge(const Vehicle *v)
Add a vehicle to the profit sum of its group.
static void CountEngine(const Vehicle *v, int delta)
Update num_engines when adding/removing an engine.
static void UpdateAutoreplace(CompanyID company)
Update autoreplace_defined and autoreplace_finished of all statistics of a company.
static void UpdateProfits()
Recompute the profits for all groups.
Group data.
Definition group.h:72
Livery livery
Custom colour scheme for vehicles in this group.
Definition group.h:78
GroupID parent
Parent group.
Definition group.h:83
Information about a particular livery.
Definition livery.h:78
Colours colour2
Second colour, for vehicles with 2CC support.
Definition livery.h:81
Colours colour1
First colour, for all vehicles.
Definition livery.h:80
uint8_t in_use
Bit 0 set if this livery should override the default livery first colour, Bit 1 for the second colour...
Definition livery.h:79
bool revalidate_before_draw
We need to do a GetImage() and check bounds before drawing this sprite.
VehicleSpriteSeq sprite_seq
Vehicle appearance.
Rect old_coord
Co-ordinates from the last valid bounding box.
static void ClearVehicle(const Vehicle *v)
Clear/update the (clone) vehicle from an order backup.
Shared order list linking together the linked list of orders and the list of vehicles sharing this or...
Definition order_base.h:258
void AddVehicle(Vehicle *v)
Adds the given vehicle to this shared order list.
Definition order_base.h:355
bool no_servicing_if_no_breakdowns
don't send vehicles to depot when breakdowns are disabled
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:144
bool Equals(const Order &other) const
Does this order have the same type, flags and destination?
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:103
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:70
CargoType GetRefitCargo() const
Get the cargo to to refit to.
Definition order_base.h:131
OrderLoadFlags GetLoadType() const
How must the consist be loaded?
Definition order_base.h:136
void MakeDummy()
Makes this order a Dummy order.
Order * next
Pointer to next order. If nullptr, end of list.
Definition order_base.h:59
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:146
bool ShouldStopAtStation(const Vehicle *v, StationID station) const
Check whether the given vehicle should stop at the given station based on this order and the non-stop...
void MakeImplicit(StationID destination)
Makes this order an implicit order.
bool IsRefit() const
Is this order a refit order.
Definition order_base.h:117
Coordinates of a point in 2D.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static size_t GetPoolSize()
Returns first unused index.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Base class for all pools.
uint8_t visual_effect
Bitstuffed NewGRF visual effect data.
Definition engine_type.h:63
EngineClass engclass
Class of engine for this vehicle.
Definition engine_type.h:59
Specification of a rectangle with absolute coordinates of all edges.
A Stop for a Road Vehicle.
void Leave(RoadVehicle *rv)
Leave the road stop.
Definition roadstop.cpp:205
RoadStop * next
Next stop of the given type at this station.
TileIndex xy
Position on the map.
static RoadStop * GetByTile(TileIndex tile, RoadStopType type)
Find a roadstop at given tile.
Definition roadstop.cpp:255
uint8_t visual_effect
Bitstuffed NewGRF visual effect data.
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint8_t state
Definition roadveh.h:100
RoadTypes compatible_roadtypes
NOSAVE: Roadtypes this consist is powered on.
Definition roadveh.h:110
bool IsBus() const
Check whether a roadvehicle is a bus.
RoadType roadtype
NOSAVE: Roadtype of this vehicle.
Definition roadveh.h:108
VehicleID disaster_vehicle
NOSAVE: Disaster vehicle targetting this vehicle.
Definition roadveh.h:109
uint8_t visual_effect
Bitstuffed NewGRF visual effect data.
Definition engine_type.h:81
All ships have this type.
Definition ship.h:32
TrackBits state
The "track" the ship is following.
Definition ship.h:34
void UpdateCache()
Update the caches of this ship.
Definition ship_cmd.cpp:232
static bool IsValidID(auto index)
Tests whether given index is a valid index for station of this type.
static Station * Get(auto index)
Gets station with given index.
static Station * GetIfValid(auto index)
Returns station if the index is a valid index for this station type.
T * Next() const
Get next vehicle in the chain.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
T * GetFirstEnginePart()
Get the first part of an articulated engine.
Data structure describing a sprite.
Definition spritecache.h:17
uint16_t width
Width of the sprite.
Definition spritecache.h:19
uint16_t height
Height of the sprite.
Definition spritecache.h:18
int16_t y_offs
Number of pixels to shift the sprite downwards.
Definition spritecache.h:21
int16_t x_offs
Number of pixels to shift the sprite to the right.
Definition spritecache.h:20
Station data structure.
std::array< GoodsEntry, NUM_CARGO > goods
Goods at this station.
Airport airport
Tile area the airport covers.
VehicleEnterTileProc * vehicle_enter_tile_proc
Called when a vehicle enters a tile.
Definition tile_cmd.h:156
'Train' is either a loco or a wagon.
Definition train.h:90
void ConsistChanged(ConsistChangeFlags allowed_changes)
Recalculates the cached stuff of a train.
uint16_t wait_counter
Ticks waiting in front of a signal, ticks being stuck or a counter for forced proceeding through sign...
Definition train.h:93
uint8_t cached_vis_effect
Visual effect to show (see VisualEffect)
uint16_t cached_cargo_age_period
Number of ticks before carried cargo is aged.
The information about a vehicle list.
Definition vehiclelist.h:32
WindowNumber ToWindowNumber() const
Pack a VehicleListIdentifier in 32 bits so it can be used as unique WindowNumber.
UnitID max_ships
max ships in game per company
UnitID max_trains
max trains in game per company
uint8_t smoke_amount
amount of smoke/sparks locomotives produce
UnitID max_aircraft
max planes in game per company
UnitID max_roadveh
max trucks in game per company
Sprite sequence for a vehicle part.
bool IsValid() const
Check whether the sequence contains any sprites.
void Draw(int x, int y, PaletteID default_pal, bool force_pal) const
Draw the sprite sequence.
Definition vehicle.cpp:142
Vehicle data structure.
Money GetDisplayProfitThisYear() const
Gets the profit vehicle had this year.
CargoPayment * cargo_payment
The cargo payment we're currently in.
EngineID engine_type
The type of engine used for this vehicle.
uint16_t cargo_age_counter
Ticks till cargo is aged next.
int32_t z_pos
z coordinate.
uint16_t & GetGroundVehicleFlags()
Access the ground vehicle flags of the vehicle.
Definition vehicle.cpp:3155
Direction direction
facing
void ShiftDates(TimerGameEconomy::Date interval)
Shift all dates by given interval.
Definition vehicle.cpp:748
TimerGameEconomy::Date economy_age
Age in economy days.
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition vehicle.cpp:718
virtual uint Crash(bool flooded=false)
Crash the (whole) vehicle chain.
Definition vehicle.cpp:291
Order * GetOrder(int index) const
Returns order 'index' of a vehicle or nullptr when it doesn't exists.
bool HasDepotOrder() const
Checks if a vehicle has a depot in its order list.
void LeaveStation()
Perform all actions when leaving a station.
Definition vehicle.cpp:2320
void AddToShared(Vehicle *shared_chain)
Adds this vehicle to a shared vehicle chain.
Definition vehicle.cpp:2938
VehicleCargoList cargo
The cargo this vehicle is carrying.
uint8_t x_extent
x-extent of vehicle bounding box
Vehicle ** hash_tile_prev
NOSAVE: Previous vehicle in the tile location hash.
bool HasUnbunchingOrder() const
Check if the current vehicle has an unbunching order.
Definition vehicle.cpp:2459
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
uint16_t cargo_cap
total capacity
StationID last_loading_station
Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.
CommandCost SendToDepot(DoCommandFlags flags, DepotCommandFlags command)
Send this vehicle to the depot using the given command(s).
Definition vehicle.cpp:2553
void ReleaseUnitNumber()
Release the vehicle's unit number.
Definition vehicle.cpp:2389
void UpdateBoundingBoxCoordinates(bool update_cache) const
Update the bounding box co-ordinates of the vehicle.
Definition vehicle.cpp:1672
uint8_t day_counter
Increased by one for each day.
void HandleLoading(bool mode=false)
Handle the loading of the vehicle; when not it skips through dummy orders and does nothing in all oth...
Definition vehicle.cpp:2400
Money profit_this_year
Profit this year << 8, low 8 bits are fract.
bool HasArticulatedPart() const
Check if an engine has an articulated part.
SpriteID colourmap
NOSAVE: cached colour mapping.
uint8_t breakdown_ctr
Counter for managing breakdown events.
uint8_t breakdown_delay
Counter for managing breakdown length.
Vehicle * GetNextVehicle() const
Get the next real (non-articulated part) vehicle in the consist.
GroupID group_id
Index of group Pool array.
void IncrementImplicitOrderIndex()
Increments cur_implicit_order_index, keeps care of the wrap-around and invalidates the GUI.
uint8_t z_extent
z-extent of vehicle bounding box
VehStates vehstatus
Status.
TimerGameCalendar::Date date_of_last_service_newgrf
Last calendar date the vehicle had a service at a depot, unchanged by the date cheat to protect again...
Vehicle * first
NOSAVE: pointer to the first vehicle in the chain.
void CancelReservation(StationID next, Station *st)
Return all reserved cargo packets to the station and reset all packets staged for transfer.
Definition vehicle.cpp:2304
Money profit_last_year
Profit last year << 8, low 8 bits are fract.
Order * GetFirstOrder() const
Get the first order of the vehicles order list.
bool IsEngineCountable() const
Check if a vehicle is counted in num_engines in each company struct.
Definition vehicle.cpp:685
virtual ~Vehicle()
We want to 'destruct' the right class.
Definition vehicle.cpp:863
bool IsArticulatedPart() const
Check if the vehicle is an articulated part of an engine.
bool NeedsAutorenewing(const Company *c, bool use_renew_setting=true) const
Function to tell if a vehicle needs to be autorenewed.
Definition vehicle.cpp:156
void UpdateVisualEffect(bool allow_power_change=true)
Update the cached visual effect.
Definition vehicle.cpp:2634
void LeaveUnbunchingDepot()
Leave an unbunching depot and calculate the next departure time for shared order vehicles.
Definition vehicle.cpp:2484
int8_t y_offs
y offset for vehicle sprite
CargoType cargo_type
type of cargo this vehicle is carrying
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
Vehicle * previous_shared
NOSAVE: pointer to the previous vehicle in the shared order chain.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
int8_t x_bb_offs
x offset of vehicle bounding box
Order current_order
The current order (+ status, like: loading)
void PreDestructor()
Destroy all stuff that (still) needs the virtual functions to work properly.
Definition vehicle.cpp:796
void HandlePathfindingResult(bool path_found)
Handle the pathfinding result, especially the lost status.
Definition vehicle.cpp:762
Vehicle * Next() const
Get the next vehicle of this vehicle.
int8_t x_offs
x offset for vehicle sprite
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
const GRFFile * GetGRF() const
Retrieve the NewGRF the vehicle is tied to.
Definition vehicle.cpp:728
OrderList * orders
Pointer to the order list for this vehicle.
uint32_t GetDisplayMinPowerToWeight() const
Calculates the minimum power-to-weight ratio using the maximum weight of the ground vehicle.
Definition vehicle.cpp:3229
uint8_t y_extent
y-extent of vehicle bounding box
void UpdateViewport(bool dirty)
Update the vehicle on the viewport, updating the right hash and setting the new coordinates.
Definition vehicle.cpp:1705
Money value
Value of the vehicle.
bool MarkAllViewportsDirty() const
Marks viewports dirty where the vehicle's image is.
Definition vehicle.cpp:1744
uint16_t refit_cap
Capacity left over from before last refit.
VehicleCache vcache
Cache of often used vehicle values.
uint32_t motion_counter
counter to occasionally play a vehicle sound.
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the vehicle is tied to.
Definition vehicle.cpp:738
GroundVehicleCache * GetGroundVehicleCache()
Access the ground vehicle cache of the vehicle.
Definition vehicle.cpp:3125
virtual void OnNewEconomyDay()
Calls the new economy day handler of the vehicle.
Vehicle ** hash_tile_current
NOSAVE: Cache of the current hash chain.
virtual void OnNewCalendarDay()
Calls the new calendar day handler of the vehicle.
Vehicle * NextShared() const
Get the next vehicle of the shared vehicle chain.
int8_t y_bb_offs
y offset of vehicle bounding box
bool HasFullLoadOrder() const
Check if the current vehicle has a full load order.
Definition vehicle.cpp:2435
virtual bool IsPrimaryVehicle() const
Whether this is the primary vehicle in the chain.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2173
Order * GetLastOrder() const
Returns the last order of a vehicle, or nullptr if it doesn't exists.
Vehicle * hash_tile_next
NOSAVE: Next vehicle in the tile location hash.
uint16_t cur_speed
current speed
Vehicle * previous
NOSAVE: pointer to the previous vehicle in the chain.
bool HasEngineType() const
Check whether Vehicle::engine_type has any meaning.
Definition vehicle.cpp:702
TimerGameCalendar::Date age
Age in calendar days.
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2531
TextEffectID fill_percent_te_id
a text-effect id to a loading indicator object
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2909
uint8_t breakdowns_since_last_service
Counter for the amount of breakdowns.
Vehicle * next
pointer to the next vehicle in the chain
TimerGameCalendar::Date max_age
Maximum age.
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
uint16_t reliability
Reliability.
uint32_t GetDisplayMaxWeight() const
Calculates the maximum weight of the ground vehicle when loaded.
Definition vehicle.cpp:3214
void RemoveFromShared()
Removes the vehicle from the shared order list.
Definition vehicle.cpp:2961
bool HandleBreakdown()
Handle all of the aspects of a vehicle breakdown This includes adding smoke and sounds,...
Definition vehicle.cpp:1333
debug_inline bool IsGroundVehicle() const
Check if the vehicle is a ground vehicle.
void UpdatePositionAndViewport()
Update the position of the vehicle, and update the viewport.
Definition vehicle.cpp:1734
Vehicle(VehicleType type=VEH_INVALID)
Vehicle constructor.
Definition vehicle.cpp:369
Vehicle * Previous() const
Get the previous vehicle of this vehicle.
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
uint16_t reliability_spd_dec
Reliability decrease speed.
uint8_t tick_counter
Increased by one for each tick.
TileIndex tile
Current tile index.
virtual bool Tick()
Calls the tick handler of the vehicle.
TileIndex dest_tile
Heading for this tile.
bool NeedsServicing() const
Check if the vehicle needs to go to a depot in near future (if a opportunity presents itself) for ser...
Definition vehicle.cpp:201
bool HasConditionalOrder() const
Check if the current vehicle has a conditional order.
Definition vehicle.cpp:2447
void UpdatePosition()
Update the position of the vehicle.
Definition vehicle.cpp:1663
StationID last_station_visited
The last station we stopped at.
void ResetRefitCaps()
Reset all refit_cap in the consist to cargo_cap.
Definition vehicle.cpp:2381
uint8_t breakdown_chance
Current chance of breakdowns.
void ShowVisualEffect() const
Draw visual effects (smoke and/or sparks) for a vehicle chain.
Definition vehicle.cpp:2759
Owner owner
Which company owns the vehicle?
Vehicle * next_shared
pointer to the next vehicle that shares the order
bool NeedsAutomaticServicing() const
Checks if the current order should be interrupted for a service-in-depot order.
Definition vehicle.cpp:283
void DeleteUnreachedImplicitOrders()
Delete all implicit orders which were not reached.
Definition vehicle.cpp:2134
Vehicle ** hash_viewport_prev
NOSAVE: Previous vehicle in the visual location hash.
virtual void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
Gets the sprite to show for the given direction.
Vehicle * hash_viewport_next
NOSAVE: Next vehicle in the visual location hash.
Data structure for viewport, display of a part of the world.
int top
Screen coordinate top edge of the viewport.
int width
Screen width of the viewport.
ZoomLevel zoom
The zoom level of the viewport.
int virtual_top
Virtual top coordinate.
int virtual_left
Virtual left coordinate.
int left
Screen coordinate left edge of the viewport.
int height
Screen height of the viewport.
VehicleEnterTileStates VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y)
Call the tile callback function for a vehicle entering a tile.
Definition vehicle.cpp:1808
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static const int MAX_VEHICLE_PIXEL_Y
Maximum height of a vehicle in pixels in ZOOM_BASE.
Definition tile_type.h:22
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static const int MAX_VEHICLE_PIXEL_X
Maximum width of a vehicle in pixels in ZOOM_BASE.
Definition tile_type.h:21
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
Definition of the tick-based game-timer.
Functions related to time tabling.
void UpdateVehicleTimetable(Vehicle *v, bool travelling)
Update the timetable for the vehicle.
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:645
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:53
Base for the train class.
@ VRF_LEAVING_STATION
Train is just leaving a station.
Definition train.h:33
@ VRF_TOGGLE_REVERSE
Used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle ...
Definition train.h:31
@ VRF_REVERSE_DIRECTION
Reverse the visible direction of the vehicle.
Definition train.h:28
@ TFP_NONE
Normal operation.
Definition train.h:38
static constexpr ConsistChangeFlags CCF_ARRANGE
Valid changes for arranging the consist in a depot.
Definition train.h:55
Command definitions related to trains.
bool IsTransparencySet(TransparencyOption to)
Check if the transparency option bit is set and if we aren't in the game menu (there's never transpar...
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
TransparencyOption
Transparency option bits: which position in _transparency_opt stands for which transparency.
@ TO_INVALID
Invalid transparency option.
uint16_t UnitID
Type for the company global vehicle unit number.
Map accessors for tunnels.
bool IsTunnelTile(Tile t)
Is this a tunnel (entrance)?
Definition tunnel_map.h:34
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBug bug_type, bool critical)
Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
Definition vehicle.cpp:328
static uint GetTileHash(uint x, uint y)
Compute hash for tile coordinate.
Definition vehicle.cpp:421
static void VehicleEnteredDepotThisTick(Vehicle *v)
Adds a vehicle to the list of vehicles that visited a depot this tick.
Definition vehicle.cpp:891
static void SpawnAdvancedVisualEffect(const Vehicle *v)
Call CBID_VEHICLE_SPAWN_VISUAL_EFFECT and spawn requested effects.
Definition vehicle.cpp:2702
static uint GetTileHash1D(uint p)
Compute hash for 1D tile coordinate.
Definition vehicle.cpp:397
void VehicleLengthChanged(const Vehicle *u)
Logs a bug in GRF and shows a warning message if this is for the first time this happened.
Definition vehicle.cpp:354
VehiclePool _vehicle_pool("Vehicle")
The pool with all our precious vehicles.
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:178
static void RunEconomyVehicleDayProc()
Increases the day counter for all vehicles and calls 1-day and 32-day handlers.
Definition vehicle.cpp:924
static uint ComposeTileHash(uint hx, uint hy)
Compose two 1D hashes into 2D hash.
Definition vehicle.cpp:413
static uint IncTileHash1D(uint h)
Increment 1D hash to next bucket.
Definition vehicle.cpp:405
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3028
Vehicle * CheckClickOnVehicle(const Viewport &vp, int x, int y)
Find the vehicle close to the clicked coordinates.
Definition vehicle.cpp:1216
std::map< VehicleID, bool > AutoreplaceMap
List of vehicles that should check for autoreplace this tick.
Definition vehicle.cpp:665
static bool PreviousOrderIsUnbunching(const Vehicle *v)
Check if the previous order is a depot unbunching order.
Definition vehicle.cpp:2471
static void DoDrawVehicle(const Vehicle *v)
Add vehicle sprite for drawing to the screen.
Definition vehicle.cpp:1092
@ VE_TYPE_DEFAULT
Use default from engine class.
@ VE_TYPE_COUNT
Number of bits used for the effect type.
@ VE_OFFSET_CENTRE
Value of offset corresponding to a position above the centre of the vehicle.
@ VE_TYPE_ELECTRIC
Electric sparks.
@ VE_TYPE_START
First bit used for the type of effect.
@ VE_OFFSET_COUNT
Number of bits used for the offset.
@ VE_ADVANCED_EFFECT
Flag for advanced effects.
@ VE_DISABLE_EFFECT
Flag to disable visual effect.
@ VE_TYPE_STEAM
Steam plumes.
@ VE_TYPE_DIESEL
Diesel fumes.
@ VE_DEFAULT
Default value to indicate that visual effect should be based on engine class.
@ VE_OFFSET_START
First bit that contains the offset (0 = front, 8 = centre, 15 = rear)
@ VE_DISABLE_WAGON_POWER
Flag to disable wagon power.
@ Unclickable
Vehicle is not clickable by the user (shadow vehicles).
@ Crashed
Vehicle is crashed.
@ Shadow
Vehicle is a shadow vehicle.
@ TrainSlowing
Train is slowing down.
@ AircraftBroken
Aircraft is broken down.
@ Hidden
Vehicle is not visible.
@ DefaultPalette
Use default vehicle palette.
@ Stopped
Vehicle is stopped by the player.
VisualEffectSpawnModel
Models for spawning visual effects.
@ VESM_ELECTRIC
Electric model.
@ VESM_STEAM
Steam model.
@ VESM_DIESEL
Diesel model.
@ VESM_NONE
No visual effect.
static const int32_t INVALID_COORD
Sentinel for an invalid coordinate.
Command definitions for vehicles.
Functions related to vehicles.
void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBug bug_type, bool critical)
Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
Definition vehicle.cpp:328
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:527
bool VehiclesHaveSameEngineList(const Vehicle *v1, const Vehicle *v2)
Checks if two vehicle chains have the same list of engines.
Definition vehicle.cpp:3242
bool VehiclesHaveSameOrderList(const Vehicle *v1, const Vehicle *v2)
Checks if two vehicles have the same list of orders.
Definition vehicle.cpp:3259
SpriteID GetEnginePalette(EngineID engine_type, CompanyID company)
Get the colour map for an engine.
Definition vehicle.cpp:2112
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1521
static const TimerGameEconomy::Date VEHICLE_PROFIT_MIN_AGE
Only vehicles older than this have a meaningful profit.
void GetVehicleSet(VehicleSet &set, Vehicle *v, uint8_t num_vehicles)
Calculates the set of vehicles that will be affected by a given selection.
Definition vehicle.cpp:3188
UnitID GetFreeUnitNumber(VehicleType type)
Get an unused unit number for a vehicle (if allowed).
Definition vehicle.cpp:1865
void RunVehicleCalendarDayProc()
Age all vehicles, spreading out the action using the current TimerGameCalendar::date_fract.
Definition vehicle.cpp:907
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:178
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:575
LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_type, const Vehicle *v)
Determines the LiveryScheme for a vehicle.
Definition vehicle.cpp:1940
void ViewportAddVehicles(DrawPixelInfo *dpi)
Add the vehicle sprites that should be drawn at a part of the screen.
Definition vehicle.cpp:1122
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1754
StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st)
Get reason string why this station can't be used by the given vehicle.
Definition vehicle.cpp:3074
void ReleaseDisasterVehicle(VehicleID vehicle)
Notify disasters that we are about to delete a vehicle.
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1271
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1399
const struct Livery * GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, uint8_t livery_setting)
Determines the livery for a vehicle.
Definition vehicle.cpp:2034
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore=nullptr)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:551
bool CanBuildVehicleInfrastructure(VehicleType type, uint8_t subtype=0)
Check whether we can build infrastructure for the given vehicle type.
Definition vehicle.cpp:1892
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1411
SpriteID GetVehiclePalette(const Vehicle *v)
Get the colour map for a vehicle.
Definition vehicle.cpp:2122
uint8_t CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
Calculates how full a vehicle is.
Definition vehicle.cpp:1462
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
@ VIWD_MODIFY_ORDERS
Other order modifications.
Definition vehicle_gui.h:36
WindowClass GetWindowClassForVehicleType(VehicleType vt)
Get WindowClass for vehicle list of given vehicle type.
Definition vehicle_gui.h:97
@ EIT_ON_MAP
Vehicle drawn in viewport.
VehicleType
Available vehicle types.
@ VEH_ROAD
Road vehicle type.
@ VEH_DISASTER
Disaster vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_EFFECT
Effect vehicle type (smoke, explosions, sparks, bubbles)
@ VEH_TRAIN
Train vehicle type.
@ Callback32
All vehicles in consist: 32 day callback requested rerandomisation.
@ Depot
Front vehicle only: Consist arrived in depot.
@ DontCancel
Don't cancel current goto depot command if any.
@ Service
The vehicle will leave the depot right after arrival (service only)
static const uint VEHICLE_LENGTH
The length of a vehicle in tile units.
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
Functions and type for generating vehicle lists.
@ VL_SHARED_ORDERS
Index is the first vehicle of the shared orders.
Definition vehiclelist.h:24
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:759
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:663
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:769
Functions related to (drawing on) viewports.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1176
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3152
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3244
void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, WidgetID widget_index)
Mark a particular widget in a particular window as dirty (in need of repainting)
Definition window.cpp:3139
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3126
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3261
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
@ WC_VEHICLE_ORDERS
Vehicle orders; Window numbers:
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_SHIPS_LIST
Ships list; Window numbers:
@ WC_STATION_VIEW
Station view; Window numbers:
@ WC_TRAINS_LIST
Trains list; Window numbers:
@ WC_VEHICLE_REFIT
Vehicle refit; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_COMPANY
Company view; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_VEHICLE_TIMETABLE
Vehicle timetable; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Functions related to zooming.
int ScaleByZoom(int value, ZoomLevel zoom)
Scale by zoom level, usually shift left (when zoom > ZoomLevel::Min) When shifting right,...
Definition zoom_func.h:22