OpenTTD Source 20251104-master-g3befbdd52f
aircraft_cmd.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
13#include "stdafx.h"
14#include "aircraft.h"
15#include "landscape.h"
16#include "news_func.h"
17#include "newgrf_engine.h"
18#include "newgrf_sound.h"
19#include "error_func.h"
20#include "strings_func.h"
21#include "command_func.h"
22#include "window_func.h"
25#include "vehicle_func.h"
26#include "sound_func.h"
27#include "cheat_type.h"
28#include "company_base.h"
29#include "ai/ai.hpp"
30#include "game/game.hpp"
31#include "company_func.h"
32#include "effectvehicle_func.h"
33#include "station_base.h"
34#include "engine_base.h"
35#include "core/random_func.hpp"
36#include "core/backup_type.hpp"
37#include "zoom_func.h"
38#include "disaster_vehicle.h"
39#include "newgrf_airporttiles.h"
40#include "framerate_type.h"
41#include "aircraft_cmd.h"
42#include "vehicle_cmd.h"
43
44#include "table/strings.h"
45
46#include "safeguards.h"
47
49{
50 this->bounds = {{-1, -1, 0}, {2, 2, 0}, {}};
51
52 switch (this->subtype) {
53 default: NOT_REACHED();
54
55 case AIR_AIRCRAFT:
56 case AIR_HELICOPTER:
57 switch (this->state) {
58 default: break;
59 case ENDTAKEOFF:
60 case LANDING:
61 case HELILANDING:
62 case FLYING:
63 /* Bounds are not centred on the aircraft. */
64 this->bounds.extent.x = 24;
65 this->bounds.extent.y = 24;
66 break;
67 }
68 this->bounds.extent.z = 5;
69 break;
70
71 case AIR_SHADOW:
72 this->bounds.extent.z = 1;
73 this->bounds.origin = {};
74 break;
75
76 case AIR_ROTOR:
77 this->bounds.extent.z = 1;
78 break;
79 }
80}
81
82static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
83static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
84static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
85static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
86static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
87static void CrashAirplane(Aircraft *v);
88
89static const SpriteID _aircraft_sprite[] = {
90 0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
91 0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
92 0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
93 0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
94 0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
95 0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
96 0x0EBD, 0x0EC5
97};
98
99template <>
100bool IsValidImageIndex<VEH_AIRCRAFT>(uint8_t image_index)
101{
102 return image_index < lengthof(_aircraft_sprite);
103}
104
106enum HelicopterRotorStates : uint8_t {
107 HRS_ROTOR_STOPPED,
108 HRS_ROTOR_MOVING_1,
109 HRS_ROTOR_MOVING_2,
110 HRS_ROTOR_MOVING_3,
111};
112
121{
122 uint best = 0;
123 StationID index = StationID::Invalid();
124 TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
125 const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
126 uint max_range = v->acache.cached_max_range_sqr;
127
128 /* Determine destinations where it's coming from and where it's heading to */
129 const Station *last_dest = nullptr;
130 const Station *next_dest = nullptr;
131 if (max_range != 0) {
132 if (v->current_order.IsType(OT_GOTO_STATION) ||
133 (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) {
135 next_dest = Station::GetIfValid(v->current_order.GetDestination().ToStationID());
136 } else {
137 last_dest = GetTargetAirportIfValid(v);
138 std::vector<StationID> next_station;
139 v->GetNextStoppingStation(next_station);
140 if (!next_station.empty()) next_dest = Station::GetIfValid(next_station.back());
141 }
142 }
143
144 for (const Station *st : Station::Iterate()) {
145 if (st->owner != v->owner || !st->facilities.Test(StationFacility::Airport) || !st->airport.HasHangar()) continue;
146
147 const AirportFTAClass *afc = st->airport.GetFTA();
148
149 /* don't crash the plane if we know it can't land at the airport */
150 if (afc->flags.Test(AirportFTAClass::Flag::ShortStrip) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue;
151
152 /* the plane won't land at any helicopter station */
153 if (!afc->flags.Test(AirportFTAClass::Flag::Airplanes) && (avi->subtype & AIR_CTOL)) continue;
154
155 /* Check if our last and next destinations can be reached from the depot airport. */
156 if (max_range != 0) {
157 uint last_dist = (last_dest != nullptr && last_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, last_dest->airport.tile) : 0;
158 uint next_dist = (next_dest != nullptr && next_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, next_dest->airport.tile) : 0;
159 if (last_dist > max_range || next_dist > max_range) continue;
160 }
161
162 /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
163 uint distance = DistanceSquare(vtile, st->airport.tile);
164 if (distance < best || index == StationID::Invalid()) {
165 best = distance;
166 index = st->index;
167 }
168 }
169 return index;
170}
171
172void Aircraft::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
173{
174 uint8_t spritenum = this->spritenum;
175
176 if (IsCustomVehicleSpriteNum(spritenum)) {
177 GetCustomVehicleSprite(this, direction, image_type, result);
178 if (result->IsValid()) return;
179
181 }
182
183 assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
184 result->Set(direction + _aircraft_sprite[spritenum]);
185}
186
187void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result)
188{
189 assert(v->subtype == AIR_HELICOPTER);
190
191 const Aircraft *w = v->Next()->Next();
192 if (IsCustomVehicleSpriteNum(v->spritenum)) {
193 GetCustomRotorSprite(v, image_type, result);
194 if (result->IsValid()) return;
195 }
196
197 /* Return standard rotor sprites if there are no custom sprites for this helicopter */
198 result->Set(SPR_ROTOR_STOPPED + w->state);
199}
200
201static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
202{
203 const Engine *e = Engine::Get(engine);
204 uint8_t spritenum = e->VehInfo<AircraftVehicleInfo>().image_index;
205
206 if (IsCustomVehicleSpriteNum(spritenum)) {
207 GetCustomVehicleIcon(engine, DIR_W, image_type, result);
208 if (result->IsValid()) return;
209
210 spritenum = e->original_image_index;
211 }
212
213 assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
214 result->Set(DIR_W + _aircraft_sprite[spritenum]);
215}
216
217void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
218{
220 GetAircraftIcon(engine, image_type, &seq);
221
222 Rect rect;
223 seq.GetBounds(&rect);
224 preferred_x = Clamp(preferred_x,
225 left - UnScaleGUI(rect.left),
226 right - UnScaleGUI(rect.right));
227
228 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
229
230 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
231 VehicleSpriteSeq rotor_seq;
232 GetCustomRotorIcon(engine, image_type, &rotor_seq);
233 if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
234 rotor_seq.Draw(preferred_x, y - ScaleSpriteTrad(5), PAL_NONE, false);
235 }
236}
237
247void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
248{
250 GetAircraftIcon(engine, image_type, &seq);
251
252 Rect rect;
253 seq.GetBounds(&rect);
254
255 width = UnScaleGUI(rect.Width());
256 height = UnScaleGUI(rect.Height());
257 xoffs = UnScaleGUI(rect.left);
258 yoffs = UnScaleGUI(rect.top);
259}
260
270{
271 const AircraftVehicleInfo *avi = &e->VehInfo<AircraftVehicleInfo>();
272 const Station *st = Station::GetByTile(tile);
273
274 /* Prevent building aircraft types at places which can't handle them */
275 if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
276
277 /* Make sure all aircraft end up in the first tile of the hangar. */
278 tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
279
280 if (flags.Test(DoCommandFlag::Execute)) {
281 Aircraft *v = new Aircraft(); // aircraft
282 Aircraft *u = new Aircraft(); // shadow
283 *ret = v;
284
285 v->direction = DIR_SE;
286
287 v->owner = u->owner = _current_company;
288
289 v->tile = tile;
290
291 uint x = TileX(tile) * TILE_SIZE + 5;
292 uint y = TileY(tile) * TILE_SIZE + 3;
293
294 v->x_pos = u->x_pos = x;
295 v->y_pos = u->y_pos = y;
296
297 u->z_pos = GetSlopePixelZ(x, y);
298 v->z_pos = u->z_pos + 1;
299
302
303 v->spritenum = avi->image_index;
304
306 v->refit_cap = 0;
307 u->refit_cap = 0;
308
310 assert(IsValidCargoType(v->cargo_type));
311
312 CargoType mail = GetCargoTypeByLabel(CT_MAIL);
313 if (IsValidCargoType(mail)) {
314 u->cargo_type = mail;
315 u->cargo_cap = avi->mail_capacity;
316 }
317
318 v->name.clear();
319 v->last_station_visited = StationID::Invalid();
320 v->last_loading_station = StationID::Invalid();
321
322 v->acceleration = avi->acceleration;
323 v->engine_type = e->index;
324 u->engine_type = e->index;
325
327 v->UpdateDeltaXY();
328
329 u->subtype = AIR_SHADOW;
330 u->UpdateDeltaXY();
331
332 v->reliability = e->reliability;
335
336 v->pos = GetVehiclePosOnBuild(tile);
337
338 v->state = HANGAR;
339 v->previous_pos = v->pos;
341 v->SetNext(u);
342
343 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
344
348
349 v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
350 u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
351
352 v->random_bits = Random();
353 u->random_bits = Random();
354
355 v->vehicle_flags = {};
357 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
358
360
361 v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
362
364
365 UpdateAircraftCache(v, true);
366
367 v->UpdatePosition();
368 u->UpdatePosition();
369
370 /* Aircraft with 3 vehicles (chopper)? */
371 if (v->subtype == AIR_HELICOPTER) {
372 Aircraft *w = new Aircraft();
373 w->engine_type = e->index;
374 w->direction = DIR_N;
376 w->x_pos = v->x_pos;
377 w->y_pos = v->y_pos;
378 w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
380 w->spritenum = 0xFF;
381 w->subtype = AIR_ROTOR;
382 w->sprite_cache.sprite_seq.Set(SPR_ROTOR_STOPPED);
383 w->random_bits = Random();
384 /* Use rotor's air.state to store the rotor animation frame */
385 w->state = HRS_ROTOR_STOPPED;
386 w->UpdateDeltaXY();
387
388 u->SetNext(w);
389 w->UpdatePosition();
390 }
391 }
392
393 return CommandCost();
394}
395
396
398{
399 const Station *st = GetTargetAirportIfValid(this);
400 /* If the station is not a valid airport or if it has no hangars */
401 if (st == nullptr || !CanVehicleUseStation(this, st) || !st->airport.HasHangar()) {
402 /* the aircraft has to search for a hangar on its own */
403 StationID station = FindNearestHangar(this);
404
405 if (station == StationID::Invalid()) return ClosestDepot();
406
407 st = Station::Get(station);
408 }
409
410 return ClosestDepot(st->xy, st->index);
411}
412
413static void CheckIfAircraftNeedsService(Aircraft *v)
414{
415 if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
416 if (v->IsChainInDepot()) {
418 return;
419 }
420
421 /* When we're parsing conditional orders and the like
422 * we don't want to consider going to a depot too. */
423 if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
424
425 const Station *st = Station::Get(v->current_order.GetDestination().ToStationID());
426
427 assert(st != nullptr);
428
429 /* only goto depot if the target airport has a depot */
430 if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
433 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
436 }
437}
438
440{
441 const Engine *e = this->GetEngine();
442 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->VehInfo<AircraftVehicleInfo>().running_cost);
443 return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
444}
445
448{
449 if (!this->IsNormalAircraft()) return;
450 AgeVehicle(this);
451}
452
455{
456 if (!this->IsNormalAircraft()) return;
457 EconomyAgeVehicle(this);
458
459 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
460
461 CheckOrders(this);
462
463 CheckVehicleBreakdown(this);
464 CheckIfAircraftNeedsService(this);
465
466 if (this->running_ticks == 0) return;
467
469
470 this->profit_this_year -= cost.GetCost();
471 this->running_ticks = 0;
472
474
477}
478
479static void HelicopterTickHandler(Aircraft *v)
480{
481 Aircraft *u = v->Next()->Next();
482
483 if (u->vehstatus.Test(VehState::Hidden)) return;
484
485 /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
486 * loading/unloading at a terminal or stopped */
487 if (v->current_order.IsType(OT_LOADING) || v->vehstatus.Test(VehState::Stopped)) {
488 if (u->cur_speed != 0) {
489 u->cur_speed++;
490 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
491 u->cur_speed = 0;
492 }
493 }
494 } else {
495 if (u->cur_speed == 0) {
496 u->cur_speed = 0x70;
497 }
498 if (u->cur_speed >= 0x50) {
499 u->cur_speed--;
500 }
501 }
502
503 int tick = ++u->tick_counter;
504 int spd = u->cur_speed >> 4;
505
507 if (spd == 0) {
508 u->state = HRS_ROTOR_STOPPED;
509 GetRotorImage(v, EIT_ON_MAP, &seq);
510 if (u->sprite_cache.sprite_seq == seq) return;
511 } else if (tick >= spd) {
512 u->tick_counter = 0;
513 u->state++;
514 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
515 GetRotorImage(v, EIT_ON_MAP, &seq);
516 } else {
517 return;
518 }
519
520 u->sprite_cache.sprite_seq = seq;
521
523}
524
532void SetAircraftPosition(Aircraft *v, int x, int y, int z)
533{
534 v->x_pos = x;
535 v->y_pos = y;
536 v->z_pos = z;
537
538 v->UpdatePosition();
539 v->UpdateViewport(true, false);
540 if (v->subtype == AIR_HELICOPTER) {
541 GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_cache.sprite_seq);
542 }
543
544 Aircraft *u = v->Next();
545
546 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
547 int safe_y = Clamp(y - 1, 0, Map::MaxY() * TILE_SIZE);
548 u->x_pos = x;
549 u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
550
551 safe_y = Clamp(u->y_pos, 0, Map::MaxY() * TILE_SIZE);
552 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
553 u->sprite_cache.sprite_seq.CopyWithoutPalette(v->sprite_cache.sprite_seq); // the shadow is never coloured
554
556
557 u = u->Next();
558 if (u != nullptr) {
559 u->x_pos = x;
560 u->y_pos = y;
561 u->z_pos = z + ROTOR_Z_OFFSET;
562
564 }
565}
566
572{
573 v->subspeed = 0;
574 v->progress = 0;
575
576 Aircraft *u = v->Next();
578 u = u->Next();
579 if (u != nullptr) {
581 u->cur_speed = 0;
582 }
583
584 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
585}
586
587static void PlayAircraftSound(const Vehicle *v)
588{
589 if (!PlayVehicleSound(v, VSE_START)) {
590 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
591 }
592}
593
594
601void UpdateAircraftCache(Aircraft *v, bool update_range)
602{
603 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
604 if (max_speed != 0) {
605 /* Convert from original units to km-ish/h */
606 max_speed = (max_speed * 128) / 10;
607
608 v->vcache.cached_max_speed = max_speed;
609 } else {
610 /* Use the default max speed of the vehicle. */
611 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
612 }
613
614 /* Update cargo aging period. */
616 Aircraft *u = v->Next(); // Shadow for mail
618
619 /* Update aircraft range. */
620 if (update_range) {
621 v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
622 /* Squared it now so we don't have to do it later all the time. */
623 v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
624 }
625}
626
627
631static constexpr uint16_t SPEED_LIMIT_TAXI = 50;
632static constexpr uint16_t SPEED_LIMIT_APPROACH = 230;
633static constexpr uint16_t SPEED_LIMIT_BROKEN = 320;
634static constexpr uint16_t SPEED_LIMIT_HOLD = 425;
635static constexpr uint16_t SPEED_LIMIT_NONE = UINT16_MAX;
636
644static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
645{
653 uint spd = v->acceleration * 77;
654 uint8_t t;
655
656 /* Adjust speed limits by plane speed factor to prevent taxiing
657 * and take-off speeds being too low. */
658 speed_limit *= _settings_game.vehicle.plane_speed;
659
660 /* adjust speed for broken vehicles */
662 if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false;
663 speed_limit = std::min<uint>(speed_limit, SPEED_LIMIT_BROKEN);
664 }
665
666 if (v->vcache.cached_max_speed < speed_limit) {
667 if (v->cur_speed < speed_limit) hard_limit = false;
668 speed_limit = v->vcache.cached_max_speed;
669 }
670
671 v->subspeed = (t = v->subspeed) + (uint8_t)spd;
672
673 /* Aircraft's current speed is used twice so that very fast planes are
674 * forced to slow down rapidly in the short distance needed. The magic
675 * value 16384 was determined to give similar results to the old speed/48
676 * method at slower speeds. This also results in less reduction at slow
677 * speeds to that aircraft do not get to taxi speed straight after
678 * touchdown. */
679 if (!hard_limit && v->cur_speed > speed_limit) {
680 speed_limit = v->cur_speed - std::max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
681 }
682
683 spd = std::min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
684
685 /* updates statusbar only if speed have changed to save CPU time */
686 if (spd != v->cur_speed) {
687 v->cur_speed = spd;
689 }
690
691 /* Adjust distance moved by plane speed setting */
693
694 /* Convert direction-independent speed into direction-dependent speed. (old movement method) */
695 spd = v->GetOldAdvanceSpeed(spd);
696
697 spd += v->progress;
698 v->progress = (uint8_t)spd;
699 return spd >> 8;
700}
701
710{
711 int safe_x = Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE);
712 int safe_y = Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE);
713 return TileHeight(TileVirtXY(safe_x, safe_y)) * TILE_HEIGHT;
714}
715
726void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
727{
728 int base_altitude = GetTileHeightBelowAircraft(v);
731 }
732
733 /* Make sure eastbound and westbound planes do not "crash" into each
734 * other by providing them with vertical separation
735 */
736 switch (v->direction) {
737 case DIR_N:
738 case DIR_NE:
739 case DIR_E:
740 case DIR_SE:
741 base_altitude += 10;
742 break;
743
744 default: break;
745 }
746
747 /* Make faster planes fly higher so that they can overtake slower ones */
748 base_altitude += std::min(20 * (v->vcache.cached_max_speed / 200) - 90, 0);
749
750 if (min_level != nullptr) *min_level = base_altitude + AIRCRAFT_MIN_FLYING_ALTITUDE;
751 if (max_level != nullptr) *max_level = base_altitude + AIRCRAFT_MAX_FLYING_ALTITUDE;
752}
753
762{
763 int tile_height = GetTileHeightBelowAircraft(v);
764
766}
767
768template <class T>
769int GetAircraftFlightLevel(T *v, bool takeoff)
770{
771 /* Aircraft is in flight. We want to enforce it being somewhere
772 * between the minimum and the maximum allowed altitude. */
773 int aircraft_min_altitude;
774 int aircraft_max_altitude;
775 GetAircraftFlightLevelBounds(v, &aircraft_min_altitude, &aircraft_max_altitude);
776 int aircraft_middle_altitude = (aircraft_min_altitude + aircraft_max_altitude) / 2;
777
778 /* If those assumptions would be violated, aircraft would behave fairly strange. */
779 assert(aircraft_min_altitude < aircraft_middle_altitude);
780 assert(aircraft_middle_altitude < aircraft_max_altitude);
781
782 int z = v->z_pos;
783 if (z < aircraft_min_altitude ||
784 (HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z < aircraft_middle_altitude)) {
785 /* Ascend. And don't fly into that mountain right ahead.
786 * And avoid our aircraft become a stairclimber, so if we start
787 * correcting altitude, then we stop correction not too early. */
789 z += takeoff ? 2 : 1;
790 } else if (!takeoff && (z > aircraft_max_altitude ||
791 (HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z > aircraft_middle_altitude))) {
792 /* Descend lower. You are an aircraft, not an space ship.
793 * And again, don't stop correcting altitude too early. */
795 z--;
796 } else if (HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z >= aircraft_middle_altitude) {
797 /* Now, we have corrected altitude enough. */
799 } else if (HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z <= aircraft_middle_altitude) {
800 /* Now, we have corrected altitude enough. */
802 }
803
804 return z;
805}
806
807template int GetAircraftFlightLevel(DisasterVehicle *v, bool takeoff);
808template int GetAircraftFlightLevel(Aircraft *v, bool takeoff);
809
824static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
825{
826 assert(v != nullptr);
827 assert(apc != nullptr);
828
829 /* In the case the station doesn't exit anymore, set target tile 0.
830 * It doesn't hurt much, aircraft will go to next order, nearest hangar
831 * or it will simply crash in next tick */
832 TileIndex tile{};
833
835 if (st != nullptr) {
836 /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
837 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
838 }
839
840 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
841 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
842
843 DiagDirection dir;
844 if (abs(delta_y) < abs(delta_x)) {
845 /* We are northeast or southwest of the airport */
846 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
847 } else {
848 /* We are northwest or southeast of the airport */
849 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
850 }
852 return apc->entry_points[dir];
853}
854
855
856static void MaybeCrashAirplane(Aircraft *v);
857
866{
867 /* nullptr if station is invalid */
869 /* INVALID_TILE if there is no station */
870 TileIndex tile = INVALID_TILE;
871 Direction rotation = DIR_N;
872 uint size_x = 1, size_y = 1;
873 if (st != nullptr) {
874 if (st->airport.tile != INVALID_TILE) {
875 tile = st->airport.tile;
876 rotation = st->airport.rotation;
877 size_x = st->airport.w;
878 size_y = st->airport.h;
879 } else {
880 tile = st->xy;
881 }
882 }
883 /* DUMMY if there is no station or no airport */
884 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
885
886 /* prevent going to INVALID_TILE if airport is deleted. */
887 if (st == nullptr || st->airport.tile == INVALID_TILE) {
888 /* Jump into our "holding pattern" state machine if possible */
889 if (v->pos >= afc->nofelements) {
890 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
891 } else if (v->targetairport != v->current_order.GetDestination()) {
892 /* If not possible, just get out of here fast */
893 v->state = FLYING;
896 /* get aircraft back on running altitude */
897 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
898 return false;
899 }
900 }
901
902 /* get airport moving data */
903 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
904
905 int x = TileX(tile) * TILE_SIZE;
906 int y = TileY(tile) * TILE_SIZE;
907
908 /* Helicopter raise */
910 Aircraft *u = v->Next()->Next();
911
912 /* Make sure the rotors don't rotate too fast */
913 if (u->cur_speed > 32) {
914 v->cur_speed = 0;
915 if (--u->cur_speed == 32) {
916 if (!PlayVehicleSound(v, VSE_START)) {
917 SoundID sfx = AircraftVehInfo(v->engine_type)->sfx;
918 /* For compatibility with old NewGRF we ignore the sfx property, unless a NewGRF-defined sound is used.
919 * The baseset has only one helicopter sound, so this only limits using plane or cow sounds. */
921 SndPlayVehicleFx(sfx, v);
922 }
923 }
924 } else {
925 u->cur_speed = 32;
926 int count = UpdateAircraftSpeed(v);
927 if (count > 0) {
928 v->tile = TileIndex{};
929
930 int z_dest;
931 GetAircraftFlightLevelBounds(v, &z_dest, nullptr);
932
933 /* Reached altitude? */
934 if (v->z_pos >= z_dest) {
935 v->cur_speed = 0;
936 return true;
937 }
938 SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z_dest));
939 }
940 }
941 return false;
942 }
943
944 /* Helicopter landing. */
947
948 if (st == nullptr) {
949 v->state = FLYING;
952 return false;
953 }
954
955 /* Vehicle is now at the airport.
956 * Helicopter has arrived at the target landing pad, so the current position is also where it should land.
957 * Except for Oilrigs which are special due to being a 1x1 station, and helicopters land outside it. */
958 if (st->airport.type != AT_OILRIG) {
959 x = v->x_pos;
960 y = v->y_pos;
961 tile = TileVirtXY(x, y);
962 }
963 v->tile = tile;
964
965 /* Find altitude of landing position. */
966 int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
967
968 if (z == v->z_pos) {
969 Vehicle *u = v->Next()->Next();
970
971 /* Increase speed of rotors. When speed is 80, we've landed. */
972 if (u->cur_speed >= 80) {
974 return true;
975 }
976 u->cur_speed += 4;
977 } else {
978 int count = UpdateAircraftSpeed(v);
979 if (count > 0) {
980 if (v->z_pos > z) {
981 SetAircraftPosition(v, v->x_pos, v->y_pos, std::max(v->z_pos - count, z));
982 } else {
983 SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z));
984 }
985 }
986 }
987 return false;
988 }
989
990 /* Get distance from destination pos to current pos. */
991 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
992
993 /* Need exact position? */
994 if (!amd.flags.Test(AirportMovingDataFlag::ExactPosition) && dist <= (amd.flags.Test(AirportMovingDataFlag::SlowTurn) ? 8U : 4U)) return true;
995
996 /* At final pos? */
997 if (dist == 0) {
998 /* Change direction smoothly to final direction. */
999 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
1000 /* if distance is 0, and plane points in right direction, no point in calling
1001 * UpdateAircraftSpeed(). So do it only afterwards */
1002 if (dirdiff == DIRDIFF_SAME) {
1003 v->cur_speed = 0;
1004 return true;
1005 }
1006
1007 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
1008
1010 v->cur_speed >>= 1;
1011
1012 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1013 return false;
1014 }
1015
1018 if (v->vehstatus.Test(VehState::Crashed)) return false;
1019 }
1020
1021 uint speed_limit = SPEED_LIMIT_TAXI;
1022 bool hard_limit = true;
1023
1025 if (amd.flags.Test(AirportMovingDataFlag::Hold)) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
1026 if (amd.flags.Test(AirportMovingDataFlag::Land)) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
1027 if (amd.flags.Test(AirportMovingDataFlag::Brake)) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
1028
1029 int count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
1030 if (count == 0) return false;
1031
1032 /* If the plane will be a few subpixels away from the destination after
1033 * this movement loop, start nudging it towards the exact position for
1034 * the whole loop. Otherwise, heavily depending on the speed of the plane,
1035 * it is possible we totally overshoot the target, causing the plane to
1036 * make a loop, and trying again, and again, and again .. */
1037 bool nudge_towards_target = static_cast<uint>(count) + 3 > dist;
1038
1039 if (v->turn_counter != 0) v->turn_counter--;
1040
1041 do {
1042
1044
1045 if (nudge_towards_target || amd.flags.Test(AirportMovingDataFlag::Land)) {
1046 /* move vehicle one pixel towards target */
1047 gp.x = (v->x_pos != (x + amd.x)) ?
1048 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
1049 v->x_pos;
1050 gp.y = (v->y_pos != (y + amd.y)) ?
1051 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
1052 v->y_pos;
1053
1054 /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
1055 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
1056
1057 } else {
1058
1059 /* Turn. Do it slowly if in the air. */
1060 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
1061 if (newdir != v->direction) {
1063 if (v->turn_counter == 0 || newdir == v->last_direction) {
1064 if (newdir == v->last_direction) {
1066 } else {
1068 }
1070 v->last_direction = v->direction;
1071 v->direction = newdir;
1072 }
1073
1074 /* Move vehicle. */
1075 gp = GetNewVehiclePos(v);
1076 } else {
1077 v->cur_speed >>= 1;
1078 v->direction = newdir;
1079
1080 /* When leaving a terminal an aircraft often goes to a position
1081 * directly in front of it. If it would move while turning it
1082 * would need an two extra turns to end up at the correct position.
1083 * To make it easier just disallow all moving while turning as
1084 * long as an aircraft is on the ground. */
1085 gp.x = v->x_pos;
1086 gp.y = v->y_pos;
1087 gp.new_tile = gp.old_tile = v->tile;
1088 }
1089 } else {
1091 /* Move vehicle. */
1092 gp = GetNewVehiclePos(v);
1093 }
1094 }
1095
1096 v->tile = gp.new_tile;
1097 /* If vehicle is in the air, use tile coordinate 0. */
1098 if (amd.flags.Any({AirportMovingDataFlag::Takeoff, AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::Land})) v->tile = TileIndex{};
1099
1100 /* Adjust Z for land or takeoff? */
1101 int z = v->z_pos;
1102
1104 z = GetAircraftFlightLevel(v, true);
1105 } else if (amd.flags.Test(AirportMovingDataFlag::Hold)) {
1106 /* Let the plane drop from normal flight altitude to holding pattern altitude */
1107 if (z > GetAircraftHoldMaxAltitude(v)) z--;
1108 } else if (amd.flags.All({AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::NoSpeedClamp})) {
1109 z = GetAircraftFlightLevel(v);
1110 }
1111
1112 /* NewGRF airports (like a rotated intercontinental from OpenGFX+Airports) can be non-rectangular
1113 * and their primary (north-most) tile does not have to be part of the airport.
1114 * As such, the height of the primary tile can be different from the rest of the airport.
1115 * Given we are landing/breaking, and as such are not a helicopter, we know that there has to be a hangar.
1116 * We also know that the airport itself has to be completely flat (otherwise it is not a valid airport).
1117 * Therefore, use the height of this hangar to calculate our z-value. */
1118 int airport_z = v->z_pos;
1119 if (amd.flags.Any({AirportMovingDataFlag::Land, AirportMovingDataFlag::Brake}) && st != nullptr) {
1120 assert(st->airport.HasHangar());
1121 TileIndex hangar_tile = st->airport.GetHangarTile(0);
1122 airport_z = GetTileMaxPixelZ(hangar_tile) + 1; // To avoid clashing with the shadow
1123 }
1124
1127 /* Zeppeliner blocked the runway, abort landing */
1128 v->state = FLYING;
1130 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
1131 v->pos = v->previous_pos;
1132 continue;
1133 }
1134
1135 if (st->airport.tile == INVALID_TILE) {
1136 /* Airport has been removed, abort the landing procedure */
1137 v->state = FLYING;
1140 /* get aircraft back on running altitude */
1141 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
1142 continue;
1143 }
1144
1145 /* We're not flying below our destination, right? */
1146 assert(airport_z <= z);
1147 int t = std::max(1U, dist - 4);
1148 int delta = z - airport_z;
1149
1150 /* Only start lowering when we're sufficiently close for a 1:1 glide */
1151 if (delta >= t) {
1152 z -= CeilDiv(z - airport_z, t);
1153 }
1154 if (z < airport_z) z = airport_z;
1155 }
1156
1157 /* We've landed. Decrease speed when we're reaching end of runway. */
1159
1160 if (z > airport_z) {
1161 z--;
1162 } else if (z < airport_z) {
1163 z++;
1164 }
1165
1166 }
1167
1168 SetAircraftPosition(v, gp.x, gp.y, z);
1169 } while (--count != 0);
1170 return false;
1171}
1172
1178{
1179 v->crashed_counter += 3;
1180
1182
1183 /* make aircraft crash down to the ground */
1184 if (v->crashed_counter < 500 && st == nullptr && ((v->crashed_counter % 3) == 0) ) {
1185 int z = GetSlopePixelZ(Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE), Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE));
1186 v->z_pos -= 1;
1187 if (v->z_pos <= z) {
1188 v->crashed_counter = 500;
1189 v->z_pos = z + 1;
1190 } else {
1191 v->crashed_counter = 0;
1192 }
1193 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1194 }
1195
1196 if (v->crashed_counter < 650) {
1197 uint32_t r;
1198 if (Chance16R(1, 32, r)) {
1199 static const DirDiff delta[] = {
1201 };
1202
1203 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
1204 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1205 r = Random();
1207 GB(r, 0, 4) - 4,
1208 GB(r, 4, 4) - 4,
1209 GB(r, 8, 4),
1211 }
1212 } else if (v->crashed_counter >= 10000) {
1213 /* remove rubble of crashed airplane */
1214
1215 /* clear runway-in on all airports, set by crashing plane
1216 * small airports use AIRPORT_BUSY, city airports use AirportBlock::RunwayInOut, etc.
1217 * but they all share the same number */
1218 if (st != nullptr) {
1219 st->airport.blocks.Reset(AirportBlock::RunwayIn);
1220 st->airport.blocks.Reset(AirportBlock::RunwayInOut); // commuter airport
1221 st->airport.blocks.Reset(AirportBlock::RunwayIn2); // intercontinental
1222 }
1223
1224 delete v;
1225
1226 return false;
1227 }
1228
1229 return true;
1230}
1231
1232
1238static void HandleAircraftSmoke(Aircraft *v, bool mode)
1239{
1240 static const struct {
1241 int8_t x;
1242 int8_t y;
1243 } smoke_pos[] = {
1244 { 5, 5 },
1245 { 6, 0 },
1246 { 5, -5 },
1247 { 0, -6 },
1248 { -5, -5 },
1249 { -6, 0 },
1250 { -5, 5 },
1251 { 0, 6 }
1252 };
1253
1254 if (!v->vehstatus.Test(VehState::AircraftBroken)) return;
1255
1256 /* Stop smoking when landed */
1257 if (v->cur_speed < 10) {
1259 v->breakdown_ctr = 0;
1260 return;
1261 }
1262
1263 /* Spawn effect et most once per Tick, i.e. !mode */
1264 if (!mode && (v->tick_counter & 0x0F) == 0) {
1266 smoke_pos[v->direction].x,
1267 smoke_pos[v->direction].y,
1268 2,
1270 );
1271 }
1272}
1273
1274void HandleMissingAircraftOrders(Aircraft *v)
1275{
1276 /*
1277 * We do not have an order. This can be divided into two cases:
1278 * 1) we are heading to an invalid station. In this case we must
1279 * find another airport to go to. If there is nowhere to go,
1280 * we will destroy the aircraft as it otherwise will enter
1281 * the holding pattern for the first airport, which can cause
1282 * the plane to go into an undefined state when building an
1283 * airport with the same StationID.
1284 * 2) we are (still) heading to a (still) valid airport, then we
1285 * can continue going there. This can happen when you are
1286 * changing the aircraft's orders while in-flight or in for
1287 * example a depot. However, when we have a current order to
1288 * go to a depot, we have to keep that order so the aircraft
1289 * actually stops.
1290 */
1291 const Station *st = GetTargetAirportIfValid(v);
1292 if (st == nullptr) {
1293 Backup<CompanyID> cur_company(_current_company, v->owner);
1295 cur_company.Restore();
1296
1297 if (ret.Failed()) CrashAirplane(v);
1298 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
1299 v->current_order.Free();
1300 }
1301}
1302
1303
1305{
1306 /* Orders are changed in flight, ensure going to the right station. */
1307 if (this->state == FLYING) {
1309 }
1310
1311 /* Aircraft do not use dest-tile */
1312 return TileIndex{};
1313}
1314
1316{
1317 this->colourmap = PAL_NONE;
1318 this->UpdateViewport(true, false);
1319 if (this->subtype == AIR_HELICOPTER) {
1320 GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_cache.sprite_seq);
1321 }
1322}
1323
1324
1325uint Aircraft::Crash(bool flooded)
1326{
1327 uint victims = Vehicle::Crash(flooded) + 2; // pilots
1328 this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
1329
1330 return victims;
1331}
1332
1338{
1340
1341 uint victims = v->Crash();
1342
1343 v->cargo.Truncate();
1344 v->Next()->cargo.Truncate();
1345 const Station *st = GetTargetAirportIfValid(v);
1346 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1347
1348 EncodedString headline;
1349 if (st == nullptr) {
1350 headline = GetEncodedString(STR_NEWS_PLANE_CRASH_OUT_OF_FUEL, victims);
1351 } else {
1352 headline = GetEncodedString(STR_NEWS_AIRCRAFT_CRASH, victims, st->index);
1353 }
1354
1355 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner));
1356 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner));
1357
1358 NewsType newstype = NewsType::Accident;
1359 if (v->owner != _local_company) {
1360 newstype = NewsType::AccidentOther;
1361 }
1362
1363 AddTileNewsItem(std::move(headline), newstype, vt, st != nullptr ? st->index : StationID::Invalid());
1364
1365 ModifyStationRatingAround(vt, v->owner, -160, 30);
1366 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1367}
1368
1374{
1375
1377
1378 uint32_t prob;
1380 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
1382 prob = 3276;
1383 } else {
1384 if (_settings_game.vehicle.plane_crashes == 0) return;
1385 prob = (0x4000 << _settings_game.vehicle.plane_crashes) / 1500;
1386 }
1387
1388 if (GB(Random(), 0, 22) > prob) return;
1389
1390 /* Crash the airplane. Remove all goods stored at the station. */
1391 for (GoodsEntry &ge : st->goods) {
1392 ge.rating = 1;
1393 if (ge.HasData()) ge.GetData().cargo.Truncate();
1394 }
1395
1396 CrashAirplane(v);
1397}
1398
1405{
1406 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
1407
1410
1411 /* Check if station was ever visited before */
1412 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
1413 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
1414 /* show newsitem of celebrating citizens */
1416 GetEncodedString(STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, st->index),
1418 v->index,
1419 st->index
1420 );
1421 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
1422 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
1423 }
1424
1425 v->BeginLoading();
1426}
1427
1433{
1435
1436 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1437
1438 v->UpdateDeltaXY();
1439
1440 TriggerAirportTileAnimation(st, vt, AirportAnimationTrigger::AirplaneTouchdown);
1441
1443 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
1444 }
1445}
1446
1447
1450{
1451 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
1452 v->targetairport = v->current_order.GetDestination().ToStationID();
1453 }
1454
1455 const Station *st = GetTargetAirportIfValid(v);
1456 const AirportFTAClass *apc = st == nullptr ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
1457 Direction rotation = st == nullptr ? DIR_N : st->airport.rotation;
1458 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
1459}
1460
1470{
1471 v->cur_speed = 0;
1472 v->subspeed = 0;
1473 v->progress = 0;
1474 v->direction = exit_dir;
1476 {
1477 Vehicle *u = v->Next();
1479
1480 /* Rotor blades */
1481 u = u->Next();
1482 if (u != nullptr) {
1484 u->cur_speed = 80;
1485 }
1486 }
1487
1490 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1493}
1494
1498static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
1499{
1501 v->state = apc->layout[v->pos].heading;
1502}
1503
1510{
1512 v->state = apc->layout[v->pos].heading;
1513}
1514
1521{
1522 /* if we just arrived, execute EnterHangar first */
1523 if (v->previous_pos != v->pos) {
1525 return;
1526 }
1527
1528 /* if we were sent to the depot, stay there */
1529 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->vehstatus.Test(VehState::Stopped)) {
1530 v->current_order.Free();
1531 return;
1532 }
1533
1534 /* Check if we should wait here for unbunching. */
1535 if (v->IsWaitingForUnbunching()) return;
1536
1537 if (!v->current_order.IsType(OT_GOTO_STATION) &&
1538 !v->current_order.IsType(OT_GOTO_DEPOT))
1539 return;
1540
1541 /* We are leaving a hangar, but have to go to the exact same one; re-enter */
1542 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
1544 return;
1545 }
1546
1547 /* if the block of the next position is busy, stay put */
1548 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1549
1550 /* We are already at the target airport, we need to find a terminal */
1552 /* FindFreeTerminal:
1553 * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
1554 if (v->subtype == AIR_HELICOPTER) {
1555 if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
1556 } else {
1557 if (!AirportFindFreeTerminal(v, apc)) return; // airplane
1558 }
1559 } else { // Else prepare for launch.
1560 /* airplane goto state takeoff, helicopter to helitakeoff */
1562 }
1563 const Station *st = Station::GetByTile(v->tile);
1565 AirportMove(v, apc);
1566}
1567
1570{
1571 /* if we just arrived, execute EnterTerminal first */
1572 if (v->previous_pos != v->pos) {
1573 AircraftEventHandler_EnterTerminal(v, apc);
1574 /* on an airport with helipads, a helicopter will always land there
1575 * and get serviced at the same time - setting */
1577 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
1578 /* an excerpt of ServiceAircraft, without the invisibility stuff */
1584 }
1585 }
1586 return;
1587 }
1588
1589 if (v->current_order.IsType(OT_NOTHING)) return;
1590
1591 /* if the block of the next position is busy, stay put */
1592 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1593
1594 /* airport-road is free. We either have to go to another airport, or to the hangar
1595 * ---> start moving */
1596
1597 bool go_to_hangar = false;
1598 switch (v->current_order.GetType()) {
1599 case OT_GOTO_STATION: // ready to fly to another airport
1600 break;
1601 case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc.
1602 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
1603 break;
1604 case OT_CONDITIONAL:
1605 /* In case of a conditional order we just have to wait a tick
1606 * longer, so the conditional order can actually be processed;
1607 * we should not clear the order as that makes us go nowhere. */
1608 return;
1609 default: // orders have been deleted (no orders), goto depot and don't bother us
1610 v->current_order.Free();
1611 go_to_hangar = true;
1612 }
1613
1614 if (go_to_hangar && Station::Get(v->targetairport)->airport.HasHangar()) {
1615 v->state = HANGAR;
1616 } else {
1617 /* airplane goto state takeoff, helicopter to helitakeoff */
1619 }
1620 AirportMove(v, apc);
1621}
1622
1623static void AircraftEventHandler_General(Aircraft *, const AirportFTAClass *)
1624{
1625 FatalError("OK, you shouldn't be here, check your Airport Scheme!");
1626}
1627
1628static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *)
1629{
1630 PlayAircraftSound(v); // play takeoffsound for airplanes
1631 v->state = STARTTAKEOFF;
1632}
1633
1634static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *)
1635{
1636 v->state = ENDTAKEOFF;
1637 v->UpdateDeltaXY();
1638}
1639
1640static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *)
1641{
1642 v->state = FLYING;
1643 /* get the next position to go to, differs per airport */
1645}
1646
1647static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *)
1648{
1649 v->state = FLYING;
1650 v->UpdateDeltaXY();
1651
1652 /* get the next position to go to, differs per airport */
1654
1655 /* Send the helicopter to a hangar if needed for replacement */
1656 if (v->NeedsAutomaticServicing()) {
1657 Backup<CompanyID> cur_company(_current_company, v->owner);
1659 cur_company.Restore();
1660 }
1661}
1662
1663static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
1664{
1666
1667 /* Runway busy, not allowed to use this airstation or closed, circle. */
1668 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !st->airport.blocks.Test(AirportBlock::AirportClosed)) {
1669 /* {32,FLYING,AirportBlock::Nothing,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
1670 * if it is an airplane, look for LANDING, for helicopter HELILANDING
1671 * it is possible to choose from multiple landing runways, so loop until a free one is found */
1672 uint8_t landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
1673 const AirportFTA *current = apc->layout[v->pos].next.get();
1674 while (current != nullptr) {
1675 if (current->heading == landingtype) {
1676 /* save speed before, since if AirportHasBlock is false, it resets them to 0
1677 * we don't want that for plane in air
1678 * hack for speed thingy */
1679 uint16_t tcur_speed = v->cur_speed;
1680 uint16_t tsubspeed = v->subspeed;
1681 if (!AirportHasBlock(v, current, apc)) {
1682 v->state = landingtype; // LANDING / HELILANDING
1684 /* it's a bit dirty, but I need to set position to next position, otherwise
1685 * if there are multiple runways, plane won't know which one it took (because
1686 * they all have heading LANDING). And also occupy that block! */
1687 v->pos = current->next_position;
1688 st->airport.blocks.Set(apc->layout[v->pos].blocks);
1689 return;
1690 }
1691 v->cur_speed = tcur_speed;
1692 v->subspeed = tsubspeed;
1693 }
1694 current = current->next.get();
1695 }
1696 }
1697 v->state = FLYING;
1698 v->pos = apc->layout[v->pos].next_position;
1699}
1700
1701static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *)
1702{
1703 v->state = ENDLANDING;
1704 AircraftLandAirplane(v); // maybe crash airplane
1705
1706 /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
1707 if (v->NeedsAutomaticServicing()) {
1708 Backup<CompanyID> cur_company(_current_company, v->owner);
1710 cur_company.Restore();
1711 }
1712}
1713
1714static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *)
1715{
1716 v->state = HELIENDLANDING;
1717 v->UpdateDeltaXY();
1718}
1719
1720static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
1721{
1722 /* next block busy, don't do a thing, just wait */
1723 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1724
1725 /* if going to terminal (OT_GOTO_STATION) choose one
1726 * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
1727 * 2. not going for terminal (but depot, no order),
1728 * --> get out of the way to the hangar. */
1729 if (v->current_order.IsType(OT_GOTO_STATION)) {
1730 if (AirportFindFreeTerminal(v, apc)) return;
1731 }
1732 v->state = HANGAR;
1733
1734}
1735
1736static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
1737{
1738 /* next block busy, don't do a thing, just wait */
1739 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1740
1741 /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
1742 * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
1743 * 2. not going for terminal (but depot, no order),
1744 * --> get out of the way to the hangar IF there are terminals on the airport.
1745 * --> else TAKEOFF
1746 * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
1747 * must go to a hangar. */
1748 if (v->current_order.IsType(OT_GOTO_STATION)) {
1749 if (AirportFindFreeHelipad(v, apc)) return;
1750 }
1752}
1753
1759typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
1762 AircraftEventHandler_General, // TO_ALL = 0
1763 AircraftEventHandler_InHangar, // HANGAR = 1
1770 AircraftEventHandler_AtTerminal, // HELIPAD1 = 8
1771 AircraftEventHandler_AtTerminal, // HELIPAD2 = 9
1772 AircraftEventHandler_TakeOff, // TAKEOFF = 10
1773 AircraftEventHandler_StartTakeOff, // STARTTAKEOFF = 11
1774 AircraftEventHandler_EndTakeOff, // ENDTAKEOFF = 12
1775 AircraftEventHandler_HeliTakeOff, // HELITAKEOFF = 13
1776 AircraftEventHandler_Flying, // FLYING = 14
1777 AircraftEventHandler_Landing, // LANDING = 15
1778 AircraftEventHandler_EndLanding, // ENDLANDING = 16
1779 AircraftEventHandler_HeliLanding, // HELILANDING = 17
1780 AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
1781 AircraftEventHandler_AtTerminal, // TERM7 = 19
1782 AircraftEventHandler_AtTerminal, // TERM8 = 20
1783 AircraftEventHandler_AtTerminal, // HELIPAD3 = 21
1784};
1785
1786static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
1787{
1788 /* we have left the previous block, and entered the new one. Free the previous block */
1789 if (apc->layout[v->previous_pos].blocks != apc->layout[v->pos].blocks) {
1791
1793 apc->layout[v->previous_pos].blocks == AirportBlock::RunwayIn) {
1794 return;
1795 }
1796
1797 st->airport.blocks.Reset(apc->layout[v->previous_pos].blocks);
1798 }
1799}
1800
1801static void AirportGoToNextPosition(Aircraft *v)
1802{
1803 /* if aircraft is not in position, wait until it is */
1804 if (!AircraftController(v)) return;
1805
1807
1808 AirportClearBlock(v, apc);
1809 AirportMove(v, apc); // move aircraft to next position
1810}
1811
1812/* gets pos from vehicle and next orders */
1813static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
1814{
1815 /* error handling */
1816 if (v->pos >= apc->nofelements) {
1817 Debug(misc, 0, "[Ap] position {} is not valid for current airport. Max position is {}", v->pos, apc->nofelements-1);
1818 assert(v->pos < apc->nofelements);
1819 }
1820
1821 const AirportFTA *current = &apc->layout[v->pos];
1822 /* we have arrived in an important state (eg terminal, hangar, etc.) */
1823 if (current->heading == v->state) {
1824 uint8_t prev_pos = v->pos; // location could be changed in state, so save it before-hand
1825 uint8_t prev_state = v->state;
1826 _aircraft_state_handlers[v->state](v, apc);
1827 if (v->state != FLYING) v->previous_pos = prev_pos;
1828 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
1829 return true;
1830 }
1831
1832 v->previous_pos = v->pos; // save previous location
1833
1834 /* there is only one choice to move to */
1835 if (current->next == nullptr) {
1836 if (AirportSetBlocks(v, current, apc)) {
1837 v->pos = current->next_position;
1839 } // move to next position
1840 return false;
1841 }
1842
1843 /* there are more choices to choose from, choose the one that
1844 * matches our heading */
1845 do {
1846 if (v->state == current->heading || current->heading == TO_ALL) {
1847 if (AirportSetBlocks(v, current, apc)) {
1848 v->pos = current->next_position;
1850 } // move to next position
1851 return false;
1852 }
1853 current = current->next.get();
1854 } while (current != nullptr);
1855
1856 Debug(misc, 0, "[Ap] cannot move further on Airport! (pos {} state {}) for vehicle {}", v->pos, v->state, v->index);
1857 NOT_REACHED();
1858}
1859
1861static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1862{
1863 const AirportFTA *reference = &apc->layout[v->pos];
1864 const AirportFTA *next = &apc->layout[current_pos->next_position];
1865
1866 /* same block, then of course we can move */
1867 if (apc->layout[current_pos->position].blocks != next->blocks) {
1868 const Station *st = Station::Get(v->targetairport);
1869 AirportBlocks blocks = next->blocks;
1870
1871 /* check additional possible extra blocks */
1872 if (current_pos != reference && current_pos->blocks != AirportBlock::Nothing) {
1873 blocks.Set(current_pos->blocks);
1874 }
1875
1876 if (st->airport.blocks.Any(blocks)) {
1877 v->cur_speed = 0;
1878 v->subspeed = 0;
1879 return true;
1880 }
1881 }
1882 return false;
1883}
1884
1892static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1893{
1894 const AirportFTA *next = &apc->layout[current_pos->next_position];
1895 const AirportFTA *reference = &apc->layout[v->pos];
1896
1897 /* if the next position is in another block, check it and wait until it is free */
1898 if (!apc->layout[current_pos->position].blocks.All(next->blocks)) {
1899 AirportBlocks blocks = next->blocks;
1900 /* search for all all elements in the list with the same state, and blocks != N
1901 * this means more blocks should be checked/set */
1902 const AirportFTA *current = current_pos;
1903 if (current == reference) current = current->next.get();
1904 while (current != nullptr) {
1905 if (current->heading == current_pos->heading && current->blocks.Any()) {
1906 blocks.Set(current->blocks);
1907 break;
1908 }
1909 current = current->next.get();
1910 }
1911
1912 /* if the block to be checked is in the next position, then exclude that from
1913 * checking, because it has been set by the airplane before */
1914 if (current_pos->blocks == next->blocks) blocks.Flip(next->blocks);
1915
1917 if (st->airport.blocks.Any(blocks)) {
1918 v->cur_speed = 0;
1919 v->subspeed = 0;
1920 return false;
1921 }
1922
1923 if (next->blocks != AirportBlock::Nothing) {
1924 st->airport.blocks.Set(blocks); // occupy next block
1925 }
1926 }
1927 return true;
1928}
1929
1938
1953
1961static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal)
1962{
1963 assert(last_terminal <= lengthof(_airport_terminal_mapping));
1965 for (; i < last_terminal; i++) {
1967 /* TERMINAL# HELIPAD# */
1968 v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
1969 st->airport.blocks.Set(_airport_terminal_mapping[i].blocks); // occupy terminal/helipad
1970 return true;
1971 }
1972 }
1973 return false;
1974}
1975
1981static uint GetNumTerminals(const AirportFTAClass *apc)
1982{
1983 uint num = 0;
1984
1985 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
1986
1987 return num;
1988}
1989
1997{
1998 /* example of more terminalgroups
1999 * {0,HANGAR,AirportBlock::Nothing,1}, {0,TERMGROUP,AirportBlock::TermGroup1,0}, {0,TERMGROUP,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
2000 * Heading TERMGROUP denotes a group. We see 2 groups here:
2001 * 1. group 0 -- AirportBlock::TermGroup1 (check block)
2002 * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
2003 * First in line is checked first, group 0. If the block (AirportBlock::TermGroup1) is free, it
2004 * looks at the corresponding terminals of that group. If no free ones are found, other
2005 * possible groups are checked (in this case group 1, since that is after group 0). If that
2006 * fails, then attempt fails and plane waits
2007 */
2008 if (apc->terminals[0] > 1) {
2009 const Station *st = Station::Get(v->targetairport);
2010 const AirportFTA *temp = apc->layout[v->pos].next.get();
2011
2012 while (temp != nullptr) {
2013 if (temp->heading == TERMGROUP) {
2014 if (!st->airport.blocks.Any(temp->blocks)) {
2015 /* read which group do we want to go to?
2016 * (the first free group) */
2017 uint target_group = temp->next_position + 1;
2018
2019 /* at what terminal does the group start?
2020 * that means, sum up all terminals of
2021 * groups with lower number */
2022 uint group_start = 0;
2023 for (uint i = 1; i < target_group; i++) {
2024 group_start += apc->terminals[i];
2025 }
2026
2027 uint group_end = group_start + apc->terminals[target_group];
2028 if (FreeTerminal(v, group_start, group_end)) return true;
2029 }
2030 } else {
2031 /* once the heading isn't 255, we've exhausted the possible blocks.
2032 * So we cannot move */
2033 return false;
2034 }
2035 temp = temp->next.get();
2036 }
2037 }
2038
2039 /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
2040 return FreeTerminal(v, 0, GetNumTerminals(apc));
2041}
2042
2050{
2051 /* if an airport doesn't have helipads, use terminals */
2052 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
2053
2054 /* only 1 helicoptergroup, check all helipads
2055 * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
2057}
2058
2064static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
2065{
2066 if (too_far) {
2067 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
2070 AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
2071 if (v->owner == _local_company) {
2072 /* Post a news message. */
2074 }
2075 }
2076 return;
2077 }
2078
2079 if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
2080 /* Not too far anymore, clear flag and message. */
2084 }
2085}
2086
2087static bool AircraftEventHandler(Aircraft *v, int loop)
2088{
2090 return HandleCrashedAircraft(v);
2091 }
2092
2093 if (v->vehstatus.Test(VehState::Stopped)) return true;
2094
2095 v->HandleBreakdown();
2096
2097 HandleAircraftSmoke(v, loop != 0);
2098 ProcessOrders(v);
2099 v->HandleLoading(loop != 0);
2100
2101 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
2102
2103 if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
2104 /* If we are flying, unconditionally clear the 'dest too far' state. */
2105 AircraftHandleDestTooFar(v, false);
2106 } else if (v->acache.cached_max_range_sqr != 0) {
2107 /* Check the distance to the next destination. This code works because the target
2108 * airport is only updated after take off and not on the ground. */
2110 Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination().ToStationID()) : nullptr;
2111
2112 if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) {
2113 uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
2115 }
2116 }
2117
2118 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
2119
2120 return true;
2121}
2122
2124{
2125 if (!this->IsNormalAircraft()) return true;
2126
2128
2129 this->tick_counter++;
2130
2131 if (!this->vehstatus.Test(VehState::Stopped)) this->running_ticks++;
2132
2133 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
2134
2135 this->current_order_time++;
2136
2137 for (uint i = 0; i != 2; i++) {
2138 /* stop if the aircraft was deleted */
2139 if (!AircraftEventHandler(this, i)) return false;
2140 }
2141
2142 return true;
2143}
2144
2145
2153{
2154 assert(v->type == VEH_AIRCRAFT);
2155
2157 if (st == nullptr) return nullptr;
2158
2159 return st->airport.tile == INVALID_TILE ? nullptr : st;
2160}
2161
2167{
2168 /* only 1 station is updated per function call, so it is enough to get entry_point once */
2169 const AirportFTAClass *ap = st->airport.GetFTA();
2170 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
2171
2172 for (Aircraft *v : Aircraft::Iterate()) {
2173 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
2174 assert(v->state == FLYING);
2175
2176 Order *o = &v->current_order;
2177 /* The aircraft is heading to a hangar, but the new station doesn't have one,
2178 * or the aircraft can't land on the new station. Cancel current order. */
2179 if (o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && o->GetDestination() == st->index &&
2180 (!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
2181 o->MakeDummy();
2183 }
2184 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
2186 }
2187
2188 /* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */
2189 if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true);
2190}
Base functions for all AIs.
Base for aircraft.
static constexpr int AIRCRAFT_MIN_FLYING_ALTITUDE
Base values for flight levels above ground level for 'normal' flight and holding patterns.
Definition aircraft.h:20
static constexpr int HELICOPTER_HOLD_MAX_FLYING_ALTITUDE
holding flying altitude above tile of helicopters.
Definition aircraft.h:23
static const int ROTOR_Z_OFFSET
Z Offset between helicopter- and rotorsprite.
Definition aircraft.h:48
static constexpr int PLANE_HOLD_MAX_FLYING_ALTITUDE
holding flying altitude above tile of planes.
Definition aircraft.h:22
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.
@ VAF_HELI_DIRECT_DESCENT
The helicopter is descending directly at its destination (helipad or in front of hangar)
Definition aircraft.h:45
@ VAF_DEST_TOO_FAR
Next destination is too far away.
Definition aircraft.h:37
@ VAF_IN_MIN_HEIGHT_CORRECTION
The vehicle is currently raising its altitude because it hit the lower bound.
Definition aircraft.h:43
@ VAF_IN_MAX_HEIGHT_CORRECTION
The vehicle is currently lowering its altitude because it hit the upper bound.
Definition aircraft.h:42
@ AIR_AIRCRAFT
an airplane
Definition aircraft.h:30
@ AIR_ROTOR
rotor of an helicopter
Definition aircraft.h:32
@ AIR_SHADOW
shadow of the aircraft
Definition aircraft.h:31
@ AIR_HELICOPTER
an helicopter
Definition aircraft.h:29
static constexpr int AIRCRAFT_MAX_FLYING_ALTITUDE
Maximum flying altitude above tile.
Definition aircraft.h:21
static void HandleAircraftSmoke(Aircraft *v, bool mode)
Handle smoke of broken aircraft.
static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
Handle aircraft movement/decision making in an airport hangar.
static StationID FindNearestHangar(const Aircraft *v)
Find the nearest hangar to v StationID::Invalid() is returned, if the company does not have any suita...
static uint GetNumTerminals(const AirportFTAClass *apc)
Get the number of terminals at the airport.
void UpdateAirplanesOnNewStation(const Station *st)
Updates the status of the Aircraft heading or in the station.
void AircraftNextAirportPos_and_Order(Aircraft *v)
set the right pos when heading to other airports after takeoff
int GetTileHeightBelowAircraft(const Vehicle *v)
Get the tile height below the aircraft.
void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
Get the size of the sprite of an aircraft sprite heading west (used for lists).
static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
Find the entry point to an airport depending on direction which the airport is being approached from.
static const MovementTerminalMapping _airport_terminal_mapping[]
A list of all valid terminals and their associated blocks.
static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
returns true if the road ahead is busy, eg.
static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
"reserve" a block for the plane
Station * GetTargetAirportIfValid(const Aircraft *v)
Returns aircraft's target station if v->target_airport is a valid station with airport.
static void AircraftLandAirplane(Aircraft *v)
Aircraft touched down at the landing strip.
int GetAircraftHoldMaxAltitude(const Aircraft *v)
Gets the maximum 'flight level' for the holding pattern of the aircraft, in pixels 'z_pos' 0,...
static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal)
Find a free terminal or helipad, and if available, assign it.
static constexpr uint16_t SPEED_LIMIT_BROKEN
Maximum speed of an aircraft that is broken.
static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
Handle the 'dest too far' flag and the corresponding news message for aircraft.
static bool AircraftController(Aircraft *v)
Controls the movement of an aircraft.
void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
Aircraft is about to leave the hangar.
static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
Find a free helipad, and assign it if available.
static constexpr uint16_t SPEED_LIMIT_NONE
No environmental speed limit. Speed limit is type dependent.
static void AircraftEntersTerminal(Aircraft *v)
Aircraft arrives at a terminal.
HelicopterRotorStates
Helicopter rotor animation states.
static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
At one of the Airport's Terminals.
void SetAircraftPosition(Aircraft *v, int x, int y, int z)
Set aircraft position.
static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit=SPEED_LIMIT_NONE, bool hard_limit=true)
Sets the new speed for an aircraft.
static void MaybeCrashAirplane(Aircraft *v)
Decide whether aircraft v should crash.
static constexpr uint16_t SPEED_LIMIT_HOLD
Maximum speed of an aircraft that flies the holding pattern.
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
Get the 'flight level' bounds, in pixels from 'z_pos' 0 for a particular vehicle for normal flight si...
static bool HandleCrashedAircraft(Aircraft *v)
Handle crashed aircraft v.
void UpdateAircraftCache(Aircraft *v, bool update_range)
Update cached values of an aircraft.
static AircraftStateHandler *const _aircraft_state_handlers[]
Array of handler functions for each target of the aircraft.
static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
Aircraft arrived in an airport hangar.
CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build an aircraft.
static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
Find a free terminal, and assign it if available.
void HandleAircraftEnterHangar(Aircraft *v)
Handle Aircraft specific tasks when an Aircraft enters a hangar.
static constexpr uint16_t SPEED_LIMIT_APPROACH
Maximum speed of an aircraft on finals.
static void CrashAirplane(Aircraft *v)
Bring the aircraft in a crashed state, create the explosion animation, and create a news item about t...
static constexpr uint16_t SPEED_LIMIT_TAXI
Special velocities for aircraft.
void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc)
Signature of the aircraft handler function.
Command definitions related to aircraft.
uint8_t GetVehiclePosOnBuild(TileIndex hangar_tile)
Get the vehicle position when an aircraft is build at the given tile.
Definition airport.cpp:197
const AirportFTAClass * GetAirport(const uint8_t airport_type)
Get the finite state machine of an airport type.
Definition airport.cpp:186
AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Direction rotation, uint num_tiles_x, uint num_tiles_y)
Rotate the airport moving data to another rotation.
Definition airport.cpp:81
static const uint MAX_TERMINALS
Some airport-related constants.
Definition airport.h:17
AirportBlock
Movement Blocks on Airports blocks (eg_airport_flags).
Definition airport.h:90
@ Zeppeliner
Block for the zeppeliner disaster vehicle.
@ Term2
Block belonging to terminal 2.
@ AirportClosed
Dummy block for indicating a closed airport.
@ Term5
Block belonging to terminal 5.
@ Term6
Block belonging to terminal 6.
@ Term4
Block belonging to terminal 4.
@ Helipad3
Block belonging to helipad 3.
@ Term7
Block belonging to terminal 7.
@ Helipad1
Block belonging to helipad 1.
@ Term3
Block belonging to terminal 3.
@ Term1
Block belonging to terminal 1.
@ Helipad2
Block belonging to helipad 2.
@ Term8
Block belonging to terminal 8.
@ HeliRaise
Helicopter take-off.
@ HeliLower
Helicopter landing.
@ Land
Landing onto landing strip.
@ Takeoff
Takeoff movement.
@ SlowTurn
Turn slowly (mostly used in the air).
@ Brake
Taxiing at the airport.
@ NoSpeedClamp
No speed restrictions.
@ Hold
Holding pattern movement (above the airport).
@ ExactPosition
Go exactly to the destination coordinates.
AirportMovementStates
Movement States on Airports (headings target)
Definition airport.h:62
@ HELITAKEOFF
Helicopter wants to leave the airport.
Definition airport.h:76
@ TERM4
Heading for terminal 4.
Definition airport.h:68
@ STARTTAKEOFF
Airplane has arrived at a runway for take-off.
Definition airport.h:74
@ HELIPAD2
Heading for helipad 2.
Definition airport.h:72
@ ENDTAKEOFF
Airplane has reached end-point of the take-off runway.
Definition airport.h:75
@ TERM5
Heading for terminal 5.
Definition airport.h:69
@ TERM6
Heading for terminal 6.
Definition airport.h:70
@ TERM3
Heading for terminal 3.
Definition airport.h:67
@ TERM8
Heading for terminal 8.
Definition airport.h:83
@ HELIPAD3
Heading for helipad 3.
Definition airport.h:84
@ HELIPAD1
Heading for helipad 1.
Definition airport.h:71
@ TERM2
Heading for terminal 2.
Definition airport.h:66
@ HANGAR
Heading for hangar.
Definition airport.h:64
@ FLYING
Vehicle is flying in the air.
Definition airport.h:77
@ TAKEOFF
Airplane wants to leave the airport.
Definition airport.h:73
@ HELILANDING
Helicopter wants to land.
Definition airport.h:80
@ TO_ALL
Go in this direction for every target.
Definition airport.h:63
@ ENDLANDING
Airplane wants to finish landing.
Definition airport.h:79
@ HELIENDLANDING
Helicopter wants to finish landing.
Definition airport.h:81
@ TERM1
Heading for terminal 1.
Definition airport.h:65
@ LANDING
Airplane wants to land.
Definition airport.h:78
@ TERM7
Heading for terminal 7.
Definition airport.h:82
@ TERMGROUP
Aircraft is looking for a free terminal in a terminalgroup.
Definition airport.h:86
@ AT_DUMMY
Dummy airport.
Definition airport.h:43
@ AT_OILRIG
Oilrig airport.
Definition airport.h:38
Class for backupping variables and making sure they are restored later.
@ BuiltAsPrototype
Vehicle is a prototype (accepted as exclusive preview).
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
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 ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
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
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool All(const Timpl &other) const
Test if all of the values are set.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Common return value for all commands.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Container for an encoded string, created by GetEncodedString.
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:50
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:200
EngineFlags flags
Flags of the engine.
Definition engine_base.h:57
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:61
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:442
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:95
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
Enum-as-bit-set wrapper.
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
RAII class for measuring multi-step 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 Year year
Current year, starting at 0.
static constexpr int DAYS_IN_YEAR
days per year
static Date date
Current date in days (day counter).
uint Truncate(uint max_move=UINT_MAX)
Truncates the cargo in this list to the given amount.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
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.
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
Subtract money from a company, including the money fraction.
Functions related to companies.
static constexpr Owner OWNER_NONE
The tile has no ownership.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection ChangeDiagDir(DiagDirection d, DiagDirDiff delta)
Applies a difference on a DiagDirection.
DiagDirDiff DiagDirDifference(DiagDirection d0, DiagDirection d1)
Calculate the difference between two DiagDirection values.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
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.
Direction
Defines the 8 directions on the map.
@ DIR_N
North.
@ DIR_SE
Southeast.
@ DIR_NE
Northeast.
@ DIR_W
West.
@ DIR_E
East.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_SW
Southwest.
All disaster vehicles.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:949
@ EXPENSES_AIRCRAFT_RUN
Running costs aircraft.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
Functions related to effect vehicles.
@ EV_EXPLOSION_SMALL
Various explosions.
@ EV_EXPLOSION_LARGE
Various explosions.
@ EV_BREAKDOWN_SMOKE_AIRCRAFT
Smoke of broken aircraft.
Base class for engines.
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
Error reporting related functions.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ PFE_GL_AIRCRAFT
Time spent processing aircraft.
Base functions for all Games.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Functions related to OTTD's landscape.
@ Random
Randomise borders.
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition map.cpp:175
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:437
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:427
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:416
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
NewGRF handling of airport tiles.
Functions for NewGRF engines.
@ PROP_AIRCRAFT_RANGE
Aircraft range.
@ PROP_AIRCRAFT_RUNNING_COST_FACTOR
Yearly runningcost.
@ PROP_AIRCRAFT_SPEED
Max. speed: 1 unit = 8 mph = 12.8 km-ish/h.
@ PROP_AIRCRAFT_CARGO_AGE_PERIOD
Number of ticks before carried cargo is aged.
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_TOUCHDOWN
Whenever a plane touches down.
@ VSE_START
Vehicle starting, i.e. leaving, the station.
Functions related to news.
void AddVehicleNewsItem(EncodedString &&headline, NewsType type, VehicleID vehicle, StationID station=StationID::Invalid())
Adds a newsitem referencing a vehicle.
Definition news_func.h:30
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.
NewsType
Type of news.
Definition news_type.h:29
@ ArrivalCompany
First vehicle arrived for company.
@ AccidentOther
An accident or disaster has occurred.
@ ArrivalOther
First vehicle arrived for competitor.
@ Accident
An accident or disaster has occurred.
@ AircraftDestinationTooFar
Next (order) destination is too far for the aircraft type.
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
Removes an order from all vehicles.
@ ODTFB_PART_OF_ORDERS
This depot order is because of a regular order.
Definition order_type.h:111
@ ODTFB_SERVICE
This depot order is because of the servicing limit.
Definition order_type.h:110
@ ODATFB_NEAREST_DEPOT
Send the vehicle to the nearest depot.
Definition order_type.h:120
Pseudo random number generator.
bool Chance16R(const uint32_t a, const uint32_t b, uint32_t &r, const std::source_location location=std::source_location::current())
Flips a coin with a given probability and saves the randomize-number in a variable.
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:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Functions related to sound.
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition sound_type.h:124
@ SND_18_TAKEOFF_HELICOPTER
22 == 0x16 Takeoff: helicopter
Definition sound_type.h:69
@ SND_17_SKID_PLANE
21 == 0x15 Plane landing / touching ground
Definition sound_type.h:68
@ SND_12_EXPLOSION
16 == 0x10 Destruction, crashes, disasters, ...
Definition sound_type.h:63
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
Base classes/functions for stations.
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
Forcibly modify station ratings near a given tile.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
@ HVOT_AIRCRAFT
Station has seen an aircraft.
@ Airport
Station with an airport.
@ AirplaneTouchdown
Triggered when an airplane (not a helicopter) touches down at the airport (for single tile).
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t cached_max_range_sqr
Cached squared maximum range.
Definition aircraft.h:65
uint16_t cached_max_range
Cached maximum range.
Definition aircraft.h:66
Information about a aircraft vehicle.
uint16_t max_speed
Maximum speed (1 unit = 8 mph = 12.8 km-ish/h)
uint8_t mail_capacity
Mail capacity (bags).
uint8_t subtype
Type of aircraft.
uint16_t passenger_capacity
Passenger capacity (persons).
uint16_t max_range
Maximum range of this aircraft.
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:72
bool Tick() override
Calls the tick handler of the vehicle.
uint8_t pos
Next desired position of the aircraft.
Definition aircraft.h:74
uint8_t state
State of the airport.
Definition aircraft.h:77
uint8_t flags
Aircraft flags.
Definition aircraft.h:81
Money GetRunningCost() const override
Gets the running cost of a vehicle.
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
uint8_t number_consecutive_turns
Protection to prevent the aircraft of making a lot of turns in order to reach a specific point.
Definition aircraft.h:79
void OnNewEconomyDay() override
Economy day handler.
uint Crash(bool flooded=false) override
Crash the (whole) vehicle chain.
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const override
Gets the sprite to show for the given direction.
void OnNewCalendarDay() override
Calendar day handler.
uint8_t turn_counter
Ticks between each turn to prevent > 45 degree turns.
Definition aircraft.h:80
TileIndex GetOrderStationLocation(StationID station) override
Determine the location for the station where the vehicle goes to next.
bool IsNormalAircraft() const
Check if the aircraft type is a normal flying device; eg not a rotor or a shadow.
Definition aircraft.h:121
void MarkDirty() override
Marks the vehicles to be redrawn and updates cached variables.
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
ClosestDepot FindClosestDepot() override
Find the closest depot for this vehicle and tell us the location, DestinationID and whether we should...
uint16_t crashed_counter
Timer for handling crash animations.
Definition aircraft.h:73
Finite sTate mAchine (FTA) of an airport.
Definition airport.h:159
const uint8_t num_helipads
Number of helipads on this airport. When 0 helicopters will go to normal terminals.
Definition airport.h:193
const AirportMovingData * MovingData(uint8_t position) const
Get movement data at a position.
Definition airport.h:184
@ Airplanes
Can planes land on this airport type?
@ ShortStrip
This airport has a short landing strip, dangerous for fast aircraft.
uint8_t delta_z
Z adjustment for helicopter pads.
Definition airport.h:197
std::vector< AirportFTA > layout
state machine for airport
Definition airport.h:191
const uint8_t * terminals
Array with the number of terminal groups, followed by the number of terminals in each group.
Definition airport.h:192
const uint8_t * entry_points
when an airplane arrives at this airport, enter it at position entry_point, index depends on directio...
Definition airport.h:196
uint8_t nofelements
number of positions the airport consists of
Definition airport.h:195
Flags flags
Flags for this airport type.
Definition airport.h:194
Internal structure used in openttd - Finite sTate mAchine --> FTA.
Definition airport.h:148
std::unique_ptr< AirportFTA > next
possible extra movement choices from this position
Definition airport.h:151
uint8_t heading
heading (current orders), guiding an airplane to its target on an airport
Definition airport.h:155
AirportBlocks blocks
bitmap of blocks that could be reserved
Definition airport.h:152
uint8_t position
the position that an airplane is at
Definition airport.h:153
uint8_t next_position
next position from this position
Definition airport.h:154
A single location on an airport where aircraft can move to.
Definition airport.h:136
int16_t x
x-coordinate of the destination.
Definition airport.h:137
AirportMovingDataFlags flags
special flags when moving towards the destination.
Definition airport.h:139
int16_t y
y-coordinate of the destination.
Definition airport.h:138
Direction direction
Direction to turn the aircraft after reaching the destination.
Definition airport.h:140
AirportBlocks blocks
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
bool HasHangar() const
Check if this airport has at least one hangar.
Direction rotation
How this airport is rotated.
uint8_t type
Type of this airport,.
Direction GetHangarExitDirection(TileIndex tile) const
Get the exit direction of the hangar at a specific tile.
const AirportFTAClass * GetFTA() const
Get the finite-state machine for this airport or the finite-state machine for the dummy airport in ca...
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
Class to backup a specific variable and restore it later.
std::string name
Name of vehicle.
TimerGameTick::Ticks current_order_time
How many ticks have passed since this order started.
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
TileIndex xy
Base tile of the station.
Owner owner
The owner of this station.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
VehicleType type
Type of vehicle.
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat no_jetcrash
no jet will crash on small airports anymore
Definition cheat_type.h:31
SoundSettings sound
sound effect settings
Structure to return information about the closest depot location, and whether it could be found.
T x
X coordinate.
T y
Y coordinate.
T z
Z coordinate.
Disasters, like submarines, skyrangers and their shadows, belong to this class.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
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.
Stores station stats for a single cargo.
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:308
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:299
Combination of aircraft state for going to a certain terminal and the airport flag for that terminal ...
AirportBlock blocks
Bitmask in the airport flags that need to be free for this terminal.
AirportMovementStates state
Aircraft movement state when going to this terminal.
VehicleSpriteSeq sprite_seq
Vehicle appearance.
bool serviceathelipad
service helicopters at helipads automatically (no need to send to depot)
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:140
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:99
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:66
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:72
void MakeDummy()
Makes this order a Dummy order.
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition order_base.h:142
void Free()
'Free' the order
Definition order_cmd.cpp:48
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS, OrderDepotActionFlags action=ODATF_SERVICE_ONLY, CargoType cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition order_cmd.cpp:74
uint16_t w
The width of the area.
TileIndex tile
The base tile of the area.
uint16_t h
The height of the area.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
Specification of a rectangle with absolute coordinates of all edges.
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
bool disaster
Play disaster and accident sounds.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
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 Aircraft * 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.
Coord3D< int8_t > origin
Position of northern corner within tile.
Definition sprite.h:19
Coord3D< uint8_t > extent
Size of bounding box.
Definition sprite.h:20
Station data structure.
std::array< GoodsEntry, NUM_CARGO > goods
Goods at this station.
Airport airport
Tile area the airport covers.
uint16_t cached_cargo_age_period
Number of ticks before carried cargo is aged.
uint16_t cached_max_speed
Maximum speed of the consist (minimum of the max speed of all vehicles in the consist).
uint8_t plane_crashes
number of plane crashes, 0 = none, 1 = reduced, 2 = normal
uint8_t plane_speed
divisor for speed of aircraft
Sprite sequence for a vehicle part.
bool IsValid() const
Check whether the sequence contains any sprites.
void GetBounds(Rect *bounds) const
Determine shared bounds of all sprites.
Definition vehicle.cpp:114
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
void CopyWithoutPalette(const VehicleSpriteSeq &src)
Copy data from another sprite sequence, while dropping all recolouring information.
void Draw(int x, int y, PaletteID default_pal, bool force_pal) const
Draw the sprite sequence.
Definition vehicle.cpp:142
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
int32_t z_pos
z coordinate.
Direction direction
facing
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
virtual bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.
VehicleCargoList cargo
The cargo this vehicle is carrying.
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.
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
uint16_t random_bits
Bits used for randomized variational spritegroups.
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:2407
Money profit_this_year
Profit this year << 8, low 8 bits are fract.
SpriteID colourmap
NOSAVE: cached colour mapping.
uint8_t breakdown_ctr
Counter for managing breakdown events.
uint GetOldAdvanceSpeed(uint speed)
Determines the effective direction-specific vehicle movement speed.
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...
uint8_t subspeed
fractional speed
void LeaveUnbunchingDepot()
Leave an unbunching depot and calculate the next departure time for shared order vehicles.
Definition vehicle.cpp:2486
void GetNextStoppingStation(std::vector< StationID > &next_station) const
Get the next station the vehicle will stop at.
CargoType cargo_type
type of cargo this vehicle is carrying
uint8_t acceleration
used by train & aircraft
Order current_order
The current order (+ status, like: loading)
Vehicle * Next() const
Get the next vehicle of this vehicle.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint16_t refit_cap
Capacity left over from before last refit.
VehicleCache vcache
Cache of often used vehicle values.
SpriteBounds bounds
Bounding box of vehicle.
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition vehicle.cpp:2183
uint8_t spritenum
currently displayed sprite index 0xfd == custom sprite, 0xfe == custom second head sprite 0xff == res...
uint16_t cur_speed
current speed
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition vehicle.cpp:2533
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2927
uint8_t breakdowns_since_last_service
Counter for the amount of breakdowns.
TimerGameCalendar::Date max_age
Maximum age.
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
uint16_t reliability
Reliability.
bool HandleBreakdown()
Handle all of the aspects of a vehicle breakdown This includes adding smoke and sounds,...
Definition vehicle.cpp:1332
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
void UpdatePositionAndViewport()
Update the position of the vehicle, and update the viewport.
Definition vehicle.cpp:1745
uint16_t reliability_spd_dec
Reliability decrease speed.
uint8_t tick_counter
Increased by one for each tick.
TileIndex tile
Current tile index.
void UpdatePosition()
Update the position of the vehicle.
Definition vehicle.cpp:1662
StationID last_station_visited
The last station we stopped at.
void InvalidateNewGRFCacheOfChain()
Invalidates cached NewGRF variables of all vehicles in the chain (after the current vehicle)
TimerGameCalendar::Year build_year
Year the vehicle has been built.
Owner owner
Which company owns the vehicle?
bool NeedsAutomaticServicing() const
Checks if the current order should be interrupted for a service-in-depot order.
Definition vehicle.cpp:283
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
static debug_inline uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
Definition of the game-calendar-timer.
Definition of the game-economy-timer.
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1520
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:178
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1765
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1270
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1398
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3046
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1410
@ Unclickable
Vehicle is not clickable by the user (shadow vehicles).
@ Crashed
Vehicle is crashed.
@ Shadow
Vehicle is a shadow vehicle.
@ AircraftBroken
Aircraft is broken down.
@ Hidden
Vehicle is not visible.
@ DefaultPalette
Use default vehicle palette.
@ Stopped
Vehicle is stopped by the player.
Command definitions for vehicles.
Functions related to vehicles.
EngineImageType
Visualisation contexts of vehicles and engines.
@ EIT_ON_MAP
Vehicle drawn in viewport.
@ VEH_AIRCRAFT
Aircraft vehicle type.
DepotCommandFlag
Flags for goto depot commands.
@ Service
The vehicle will leave the depot right after arrival (service only)
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition window.cpp:3184
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:3276
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:3171
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3158
Window functions not directly related to making/drawing windows.
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107
int UnScaleGUI(int value)
Short-hand to apply GUI zoom level.
Definition zoom_func.h:77