OpenTTD Source 20260731-master-g77ba2b244a
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "aircraft.h"
12#include "landscape.h"
13#include "news_func.h"
14#include "newgrf_engine.h"
15#include "newgrf_sound.h"
16#include "error_func.h"
17#include "strings_func.h"
18#include "command_func.h"
19#include "window_func.h"
22#include "vehicle_func.h"
23#include "sound_func.h"
24#include "cheat_type.h"
25#include "company_base.h"
26#include "ai/ai.hpp"
27#include "game/game.hpp"
28#include "company_func.h"
29#include "effectvehicle_func.h"
30#include "station_base.h"
31#include "engine_base.h"
32#include "core/random_func.hpp"
33#include "core/backup_type.hpp"
34#include "zoom_func.h"
35#include "disaster_vehicle.h"
36#include "newgrf_airporttiles.h"
37#include "framerate_type.h"
38#include "aircraft_cmd.h"
39#include "vehicle_cmd.h"
40#include "script/api/script_event_types.hpp"
41
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
100template <>
102{
103 return image_index < lengthof(_aircraft_sprite);
104}
105
107enum HelicopterRotorStates : uint8_t {
108 HRS_ROTOR_STOPPED,
109 HRS_ROTOR_MOVING_1,
110 HRS_ROTOR_MOVING_2,
111 HRS_ROTOR_MOVING_3,
112};
113
121static StationID FindNearestHangar(const Aircraft *v)
122{
123 uint best = 0;
124 StationID index = StationID::Invalid();
126 const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
127 uint max_range = v->acache.cached_max_range_sqr;
128
129 /* Determine destinations where it's coming from and where it's heading to */
130 const Station *last_dest = nullptr;
131 const Station *next_dest = nullptr;
132 if (max_range != 0) {
133 if (v->current_order.IsType(OT_GOTO_STATION) ||
136 next_dest = Station::GetIfValid(v->current_order.GetDestination().ToStationID());
137 } else {
138 last_dest = GetTargetAirportIfValid(v);
139 std::vector<StationID> next_station;
140 v->GetNextStoppingStation(next_station);
141 if (!next_station.empty()) next_dest = Station::GetIfValid(next_station.back());
142 }
143 }
144
145 for (const Station *st : Station::Iterate()) {
146 if (st->owner != v->owner || !st->facilities.Test(StationFacility::Airport) || !st->airport.HasHangar()) continue;
147
148 const AirportFTAClass *afc = st->airport.GetFTA();
149
150 /* don't crash the plane if we know it can't land at the airport */
151 if (afc->flags.Test(AirportFTAClass::Flag::ShortStrip) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue;
152
153 /* the plane won't land at any helicopter station */
154 if (!afc->flags.Test(AirportFTAClass::Flag::Airplanes) && (avi->subtype & AIR_CTOL)) continue;
155
156 /* Check if our last and next destinations can be reached from the depot airport. */
157 if (max_range != 0) {
158 uint last_dist = (last_dest != nullptr && last_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, last_dest->airport.tile) : 0;
159 uint next_dist = (next_dest != nullptr && next_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, next_dest->airport.tile) : 0;
160 if (last_dist > max_range || next_dist > max_range) continue;
161 }
162
163 /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
164 uint distance = DistanceSquare(vtile, st->airport.tile);
165 if (distance < best || index == StationID::Invalid()) {
166 best = distance;
167 index = st->index;
168 }
169 }
170 return index;
171}
172
174{
175 uint8_t spritenum = this->spritenum;
176
177 if (IsCustomVehicleSpriteNum(spritenum)) {
178 GetCustomVehicleSprite(this, direction, image_type, result);
179 if (result->IsValid()) return;
180
182 }
183
185 result->Set(to_underlying(direction) + _aircraft_sprite[spritenum]);
186}
187
188void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result)
189{
190 assert(v->subtype == AIR_HELICOPTER);
191
192 const Aircraft *w = v->Next()->Next();
193 if (IsCustomVehicleSpriteNum(v->spritenum)) {
194 GetCustomRotorSprite(v, image_type, result);
195 if (result->IsValid()) return;
196 }
197
198 /* Return standard rotor sprites if there are no custom sprites for this helicopter */
199 result->Set(SPR_ROTOR_STOPPED + w->state);
200}
201
202static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
203{
204 const Engine *e = Engine::Get(engine);
205 uint8_t spritenum = e->VehInfo<AircraftVehicleInfo>().image_index;
206
207 if (IsCustomVehicleSpriteNum(spritenum)) {
208 GetCustomVehicleIcon(engine, Direction::W, image_type, result);
209 if (result->IsValid()) return;
210
211 spritenum = e->original_image_index;
212 }
213
215 result->Set(to_underlying(Direction::W) + _aircraft_sprite[spritenum]);
216}
217
218void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
219{
221 GetAircraftIcon(engine, image_type, &seq);
222
223 Rect rect;
224 seq.GetBounds(&rect);
225 preferred_x = Clamp(preferred_x,
226 left - UnScaleGUI(rect.left),
227 right - UnScaleGUI(rect.right));
228
229 seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
230
231 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
232 VehicleSpriteSeq rotor_seq;
233 GetCustomRotorIcon(engine, image_type, &rotor_seq);
234 if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
235 rotor_seq.Draw(preferred_x, y - ScaleSpriteTrad(5), PAL_NONE, false);
236 }
237}
238
248void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
249{
251 GetAircraftIcon(engine, image_type, &seq);
252
253 Rect rect;
254 seq.GetBounds(&rect);
255
256 width = UnScaleGUI(rect.Width());
257 height = UnScaleGUI(rect.Height());
258 xoffs = UnScaleGUI(rect.left);
259 yoffs = UnScaleGUI(rect.top);
260}
261
271{
272 const AircraftVehicleInfo *avi = &e->VehInfo<AircraftVehicleInfo>();
273 const Station *st = Station::GetByTile(tile);
274
275 /* Prevent building aircraft types at places which can't handle them */
276 if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
277
278 /* Make sure all aircraft end up in the first tile of the hangar. */
279 tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
280
281 if (flags.Test(DoCommandFlag::Execute)) {
282 Aircraft *v = Aircraft::Create(); // aircraft
283 Aircraft *u = Aircraft::Create(); // shadow
284 *ret = v;
285
287
288 v->owner = u->owner = _current_company;
289
290 v->tile = tile;
291
292 uint x = TileX(tile) * TILE_SIZE + 5;
293 uint y = TileY(tile) * TILE_SIZE + 3;
294
295 v->x_pos = u->x_pos = x;
296 v->y_pos = u->y_pos = y;
297
298 u->z_pos = GetSlopePixelZ(x, y);
299 v->z_pos = u->z_pos + 1;
300
303
304 v->spritenum = avi->image_index;
305
307 v->refit_cap = 0;
308 u->refit_cap = 0;
309
311 assert(IsValidCargoType(v->cargo_type));
312
313 CargoType mail = GetCargoTypeByLabel(CT_MAIL);
314 if (IsValidCargoType(mail)) {
315 u->cargo_type = mail;
316 u->cargo_cap = avi->mail_capacity;
317 }
318
319 v->name.clear();
320 v->last_station_visited = StationID::Invalid();
321 v->last_loading_station = StationID::Invalid();
322
323 v->acceleration = avi->acceleration;
324 v->engine_type = e->index;
325 u->engine_type = e->index;
326
328 v->UpdateDeltaXY();
329
330 u->subtype = AIR_SHADOW;
331 u->UpdateDeltaXY();
332
333 v->reliability = e->reliability;
336
337 v->pos = GetVehiclePosOnBuild(tile);
338
339 v->state = HANGAR;
340 v->previous_pos = v->pos;
342 v->SetNext(u);
343
344 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
345
349
352
353 v->random_bits = Random();
354 u->random_bits = Random();
355
356 v->vehicle_flags = {};
358 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
359
361
362 v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
363
365
366 UpdateAircraftCache(v, true);
367
368 v->UpdatePosition();
369 u->UpdatePosition();
370
371 /* Aircraft with 3 vehicles (chopper)? */
372 if (v->subtype == AIR_HELICOPTER) {
374 w->engine_type = e->index;
377 w->x_pos = v->x_pos;
378 w->y_pos = v->y_pos;
379 w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
381 w->spritenum = 0xFF;
382 w->subtype = AIR_ROTOR;
384 w->random_bits = Random();
385 /* Use rotor's air.state to store the rotor animation frame */
386 w->state = HRS_ROTOR_STOPPED;
387 w->UpdateDeltaXY();
388
389 u->SetNext(w);
390 w->UpdatePosition();
391 }
392 }
393
394 return CommandCost();
395}
396
397
399{
400 const Station *st = GetTargetAirportIfValid(this);
401 /* If the station is not a valid airport or if it has no hangars */
402 if (st == nullptr || !CanVehicleUseStation(this, st) || !st->airport.HasHangar()) {
403 /* the aircraft has to search for a hangar on its own */
404 StationID station = FindNearestHangar(this);
405
406 if (station == StationID::Invalid()) return ClosestDepot();
407
408 st = Station::Get(station);
409 }
410
411 return ClosestDepot(st->xy, st->index);
412}
413
414static void CheckIfAircraftNeedsService(Aircraft *v)
415{
416 if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
417 if (v->IsChainInDepot()) {
419 return;
420 }
421
422 /* When we're parsing conditional orders and the like
423 * we don't want to consider going to a depot too. */
424 if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
425
426 const Station *st = Station::Get(v->current_order.GetDestination().ToStationID());
427
428 assert(st != nullptr);
429
430 /* only goto depot if the target airport has a depot */
431 if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
433 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
434 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
436 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
437 }
438}
439
441{
442 const Engine *e = this->GetEngine();
443 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->VehInfo<AircraftVehicleInfo>().running_cost);
444 return GetPrice(Price::RunningAircraft, cost_factor, e->GetGRF());
445}
446
449{
450 if (!this->IsNormalAircraft()) return;
451 AgeVehicle(this);
452}
453
456{
457 if (!this->IsNormalAircraft()) return;
458 EconomyAgeVehicle(this);
459
460 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
461
462 CheckOrders(this);
463
465 CheckIfAircraftNeedsService(this);
466
467 if (this->running_ticks == 0) return;
468
470
471 this->profit_this_year -= cost.GetCost();
472 this->running_ticks = 0;
473
475
476 SetWindowDirty(WindowClass::VehicleDetails, this->index);
477 SetWindowClassesDirty(WindowClass::AircraftList);
478}
479
480static void HelicopterTickHandler(Aircraft *v)
481{
482 Aircraft *u = v->Next()->Next();
483
484 if (u->vehstatus.Test(VehState::Hidden)) return;
485
486 /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
487 * loading/unloading at a terminal or stopped */
488 if (v->current_order.IsType(OT_LOADING) || v->vehstatus.Test(VehState::Stopped)) {
489 if (u->cur_speed != 0) {
490 u->cur_speed++;
491 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
492 u->cur_speed = 0;
493 }
494 }
495 } else {
496 if (u->cur_speed == 0) {
497 u->cur_speed = 0x70;
498 }
499 if (u->cur_speed >= 0x50) {
500 u->cur_speed--;
501 }
502 }
503
504 int tick = ++u->tick_counter;
505 int spd = u->cur_speed >> 4;
506
508 if (spd == 0) {
509 u->state = HRS_ROTOR_STOPPED;
510 GetRotorImage(v, EngineImageType::OnMap, &seq);
511 if (u->sprite_cache.sprite_seq == seq) return;
512 } else if (tick >= spd) {
513 u->tick_counter = 0;
514 u->state++;
515 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
516 GetRotorImage(v, EngineImageType::OnMap, &seq);
517 } else {
518 return;
519 }
520
521 u->sprite_cache.sprite_seq = seq;
522
524}
525
533void SetAircraftPosition(Aircraft *v, int x, int y, int z)
534{
535 v->x_pos = x;
536 v->y_pos = y;
537 v->z_pos = z;
538
539 v->UpdatePosition();
540 v->UpdateViewport(true, false);
541 if (v->subtype == AIR_HELICOPTER) {
542 GetRotorImage(v, EngineImageType::OnMap, &v->Next()->Next()->sprite_cache.sprite_seq);
543 }
544
545 Aircraft *u = v->Next();
546
547 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
548 int safe_y = Clamp(y - 1, 0, Map::MaxY() * TILE_SIZE);
549 u->x_pos = x;
550 u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
551
552 safe_y = Clamp(u->y_pos, 0, Map::MaxY() * TILE_SIZE);
553 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
554 u->sprite_cache.sprite_seq.CopyWithoutPalette(v->sprite_cache.sprite_seq); // the shadow is never coloured
555
557
558 u = u->Next();
559 if (u != nullptr) {
560 u->x_pos = x;
561 u->y_pos = y;
562 u->z_pos = z + ROTOR_Z_OFFSET;
563
565 }
566}
567
573{
574 v->subspeed = 0;
575 v->progress = 0;
576
577 Aircraft *u = v->Next();
579 u = u->Next();
580 if (u != nullptr) {
582 u->cur_speed = 0;
583 }
584
585 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
586}
587
588static void PlayAircraftSound(const Vehicle *v)
589{
590 if (!PlayVehicleSound(v, VSE_START)) {
591 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
592 }
593}
594
595
602void UpdateAircraftCache(Aircraft *v, bool update_range)
603{
604 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
605 if (max_speed != 0) {
606 /* Convert from original units to km-ish/h */
607 max_speed = (max_speed * 128) / 10;
608
609 v->vcache.cached_max_speed = max_speed;
610 } else {
611 /* Use the default max speed of the vehicle. */
612 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
613 }
614
615 /* Update cargo aging period. */
616 v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
617 Aircraft *u = v->Next(); // Shadow for mail
618 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
619
620 /* Update aircraft range. */
621 if (update_range) {
622 v->acache.cached_max_range = Engine::Get(v->engine_type)->GetRange();
623 /* Squared it now so we don't have to do it later all the time. */
624 v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
625 }
626}
627
628
632static constexpr uint16_t SPEED_LIMIT_TAXI = 50;
633static constexpr uint16_t SPEED_LIMIT_APPROACH = 230;
634static constexpr uint16_t SPEED_LIMIT_BROKEN = 320;
635static constexpr uint16_t SPEED_LIMIT_HOLD = 425;
636static constexpr uint16_t SPEED_LIMIT_NONE = UINT16_MAX;
637
645static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
646{
654 uint spd = v->acceleration * 77;
655 uint8_t t;
656
657 /* Adjust speed limits by plane speed factor to prevent taxiing
658 * and take-off speeds being too low. */
659 speed_limit *= _settings_game.vehicle.plane_speed;
660
661 /* adjust speed for broken vehicles */
663 if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false;
664 speed_limit = std::min<uint>(speed_limit, SPEED_LIMIT_BROKEN);
665 }
666
667 if (v->vcache.cached_max_speed < speed_limit) {
668 if (v->cur_speed < speed_limit) hard_limit = false;
669 speed_limit = v->vcache.cached_max_speed;
670 }
671
672 v->subspeed = (t = v->subspeed) + (uint8_t)spd;
673
674 /* Aircraft's current speed is used twice so that very fast planes are
675 * forced to slow down rapidly in the short distance needed. The magic
676 * value 16384 was determined to give similar results to the old speed/48
677 * method at slower speeds. This also results in less reduction at slow
678 * speeds to that aircraft do not get to taxi speed straight after
679 * touchdown. */
680 if (!hard_limit && v->cur_speed > speed_limit) {
681 speed_limit = v->cur_speed - std::max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
682 }
683
684 spd = std::min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
685
686 /* updates statusbar only if speed have changed to save CPU time */
687 if (spd != v->cur_speed) {
688 v->cur_speed = spd;
689 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
690 }
691
692 /* Adjust distance moved by plane speed setting */
693 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
694
695 /* Convert direction-independent speed into direction-dependent speed. (old movement method) */
696 spd = v->GetOldAdvanceSpeed(spd);
697
698 spd += v->progress;
699 v->progress = (uint8_t)spd;
700 return spd >> 8;
701}
702
711{
712 int safe_x = Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE);
713 int safe_y = Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE);
714 return TileHeight(TileVirtXY(safe_x, safe_y)) * TILE_HEIGHT;
715}
716
727void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
728{
729 int base_altitude = GetTileHeightBelowAircraft(v);
730 if (v->type == VehicleType::Aircraft && Aircraft::From(v)->subtype == AIR_HELICOPTER) {
732 }
733
734 /* Make sure eastbound and westbound planes do not "crash" into each
735 * other by providing them with vertical separation
736 */
737 switch (v->direction) {
738 case Direction::N:
739 case Direction::NE:
740 case Direction::E:
741 case Direction::SE:
742 base_altitude += 10;
743 break;
744
745 default: break;
746 }
747
748 /* Make faster planes fly higher so that they can overtake slower ones */
749 base_altitude += std::min(20 * (v->vcache.cached_max_speed / 200) - 90, 0);
750
751 if (min_level != nullptr) *min_level = base_altitude + AIRCRAFT_MIN_FLYING_ALTITUDE;
752 if (max_level != nullptr) *max_level = base_altitude + AIRCRAFT_MAX_FLYING_ALTITUDE;
753}
754
763{
764 int tile_height = GetTileHeightBelowAircraft(v);
765
767}
768
769template <class T>
770int GetAircraftFlightLevel(T *v, bool takeoff)
771{
772 /* Aircraft is in flight. We want to enforce it being somewhere
773 * between the minimum and the maximum allowed altitude. */
774 int aircraft_min_altitude;
775 int aircraft_max_altitude;
776 GetAircraftFlightLevelBounds(v, &aircraft_min_altitude, &aircraft_max_altitude);
777 int aircraft_middle_altitude = (aircraft_min_altitude + aircraft_max_altitude) / 2;
778
779 /* If those assumptions would be violated, aircraft would behave fairly strange. */
780 assert(aircraft_min_altitude < aircraft_middle_altitude);
781 assert(aircraft_middle_altitude < aircraft_max_altitude);
782
783 int z = v->z_pos;
784 if (z < aircraft_min_altitude ||
785 (v->flags.Test(VehicleAirFlag::InMinimumHeightCorrection) && z < aircraft_middle_altitude)) {
786 /* Ascend. And don't fly into that mountain right ahead.
787 * And avoid our aircraft become a stairclimber, so if we start
788 * correcting altitude, then we stop correction not too early. */
790 z += takeoff ? 2 : 1;
791 } else if (!takeoff && (z > aircraft_max_altitude ||
792 (v->flags.Test(VehicleAirFlag::InMaximumHeightCorrection) && z > aircraft_middle_altitude))) {
793 /* Descend lower. You are an aircraft, not an space ship.
794 * And again, don't stop correcting altitude too early. */
796 z--;
797 } else if (v->flags.Test(VehicleAirFlag::InMinimumHeightCorrection) && z >= aircraft_middle_altitude) {
798 /* Now, we have corrected altitude enough. */
800 } else if (v->flags.Test(VehicleAirFlag::InMaximumHeightCorrection) && z <= aircraft_middle_altitude) {
801 /* Now, we have corrected altitude enough. */
803 }
804
805 return z;
806}
807
808template int GetAircraftFlightLevel(DisasterVehicle *v, bool takeoff);
809template int GetAircraftFlightLevel(Aircraft *v, bool takeoff);
810
825static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
826{
827 assert(v != nullptr);
828 assert(apc != nullptr);
829
830 /* In the case the station doesn't exit anymore, set target tile 0.
831 * It doesn't hurt much, aircraft will go to next order, nearest hangar
832 * or it will simply crash in next tick */
833 TileIndex tile{};
834
836 if (st != nullptr) {
837 /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
838 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
839 }
840
841 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
842 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
843
844 DiagDirection dir;
845 if (abs(delta_y) < abs(delta_x)) {
846 /* We are northeast or southwest of the airport */
847 dir = delta_x < 0 ? DiagDirection::NE : DiagDirection::SW;
848 } else {
849 /* We are northwest or southeast of the airport */
850 dir = delta_y < 0 ? DiagDirection::NW : DiagDirection::SE;
851 }
853 return apc->entry_points[to_underlying(dir)];
854}
855
856
857static void MaybeCrashAirplane(Aircraft *v);
858
867{
868 /* nullptr if station is invalid */
870 /* INVALID_TILE if there is no station */
871 TileIndex tile = INVALID_TILE;
872 Direction rotation = Direction::N;
873 uint size_x = 1, size_y = 1;
874 if (st != nullptr) {
875 if (st->airport.tile != INVALID_TILE) {
876 tile = st->airport.tile;
877 rotation = st->airport.rotation;
878 size_x = st->airport.w;
879 size_y = st->airport.h;
880 } else {
881 tile = st->xy;
882 }
883 }
884 /* DUMMY if there is no station or no airport */
885 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
886
887 /* prevent going to INVALID_TILE if airport is deleted. */
888 if (st == nullptr || st->airport.tile == INVALID_TILE) {
889 /* Jump into our "holding pattern" state machine if possible */
890 if (v->pos >= afc->nofelements) {
892 } else if (v->targetairport != v->current_order.GetDestination()) {
893 /* If not possible, just get out of here fast */
894 v->state = FLYING;
897 /* get aircraft back on running altitude */
898 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
899 return false;
900 }
901 }
902
903 /* get airport moving data */
904 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
905
906 int x = TileX(tile) * TILE_SIZE;
907 int y = TileY(tile) * TILE_SIZE;
908
909 /* Helicopter raise */
911 Aircraft *u = v->Next()->Next();
912
913 /* Make sure the rotors don't rotate too fast */
914 if (u->cur_speed > 32) {
915 v->cur_speed = 0;
916 if (--u->cur_speed == 32) {
917 if (!PlayVehicleSound(v, VSE_START)) {
918 SoundID sfx = AircraftVehInfo(v->engine_type)->sfx;
919 /* For compatibility with old NewGRF we ignore the sfx property, unless a NewGRF-defined sound is used.
920 * The baseset has only one helicopter sound, so this only limits using plane or cow sounds. */
922 SndPlayVehicleFx(sfx, v);
923 }
924 }
925 } else {
926 u->cur_speed = 32;
927 int count = UpdateAircraftSpeed(v);
928 if (count > 0) {
929 v->tile = TileIndex{};
930
931 int z_dest;
932 GetAircraftFlightLevelBounds(v, &z_dest, nullptr);
933
934 /* Reached altitude? */
935 if (v->z_pos >= z_dest) {
936 v->cur_speed = 0;
937 return true;
938 }
939 SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z_dest));
940 }
941 }
942 return false;
943 }
944
945 /* Helicopter landing. */
948
949 if (st == nullptr) {
950 v->state = FLYING;
953 return false;
954 }
955
956 /* Vehicle is now at the airport.
957 * Helicopter has arrived at the target landing pad, so the current position is also where it should land.
958 * Except for Oilrigs which are special due to being a 1x1 station, and helicopters land outside it. */
959 if (st->airport.type != AT_OILRIG) {
960 x = v->x_pos;
961 y = v->y_pos;
962 tile = TileVirtXY(x, y);
963 }
964 v->tile = tile;
965
966 /* Find altitude of landing position. */
967 int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
968
969 if (z == v->z_pos) {
970 Vehicle *u = v->Next()->Next();
971
972 /* Increase speed of rotors. When speed is 80, we've landed. */
973 if (u->cur_speed >= 80) {
975 return true;
976 }
977 u->cur_speed += 4;
978 } else {
979 int count = UpdateAircraftSpeed(v);
980 if (count > 0) {
981 if (v->z_pos > z) {
982 SetAircraftPosition(v, v->x_pos, v->y_pos, std::max(v->z_pos - count, z));
983 } else {
984 SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z));
985 }
986 }
987 }
988 return false;
989 }
990
991 /* Get distance from destination pos to current pos. */
992 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
993
994 /* Need exact position? */
995 if (!amd.flags.Test(AirportMovingDataFlag::ExactPosition) && dist <= (amd.flags.Test(AirportMovingDataFlag::SlowTurn) ? 8U : 4U)) return true;
996
997 /* At final pos? */
998 if (dist == 0) {
999 /* Change direction smoothly to final direction. */
1000 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
1001 /* if distance is 0, and plane points in right direction, no point in calling
1002 * UpdateAircraftSpeed(). So do it only afterwards */
1003 if (dirdiff == DirDiff::Same) {
1004 v->cur_speed = 0;
1005 return true;
1006 }
1007
1008 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
1009
1010 v->direction = ChangeDir(v->direction, LimitDirDiff(dirdiff));
1011 v->cur_speed >>= 1;
1012
1013 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1014 return false;
1015 }
1016
1017 if (amd.flags.Test(AirportMovingDataFlag::Brake) && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
1019 if (v->vehstatus.Test(VehState::Crashed)) return false;
1020 }
1021
1022 uint speed_limit = SPEED_LIMIT_TAXI;
1023 bool hard_limit = true;
1024
1026 speed_limit = SPEED_LIMIT_NONE;
1027 }
1029 speed_limit = SPEED_LIMIT_HOLD;
1030 hard_limit = false;
1031 }
1033 speed_limit = SPEED_LIMIT_APPROACH;
1034 hard_limit = false;
1035 }
1037 speed_limit = SPEED_LIMIT_TAXI;
1038 hard_limit = false;
1039 }
1040
1041 int count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
1042 if (count == 0) return false;
1043
1044 /* If the plane will be a few subpixels away from the destination after
1045 * this movement loop, start nudging it towards the exact position for
1046 * the whole loop. Otherwise, heavily depending on the speed of the plane,
1047 * it is possible we totally overshoot the target, causing the plane to
1048 * make a loop, and trying again, and again, and again .. */
1049 bool nudge_towards_target = static_cast<uint>(count) + 3 > dist;
1050
1051 if (v->turn_counter != 0) v->turn_counter--;
1052
1053 do {
1054
1056
1057 if (nudge_towards_target || amd.flags.Test(AirportMovingDataFlag::Land)) {
1058 /* move vehicle one pixel towards target */
1059 gp.x = (v->x_pos != (x + amd.x)) ?
1060 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
1061 v->x_pos;
1062 gp.y = (v->y_pos != (y + amd.y)) ?
1063 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
1064 v->y_pos;
1065
1066 /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
1067 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
1068
1069 } else {
1070
1071 /* Turn. Do it slowly if in the air. */
1072 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
1073 if (newdir != v->direction) {
1075 if (v->turn_counter == 0 || newdir == v->last_direction) {
1076 if (newdir == v->last_direction) {
1078 } else {
1080 }
1081 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
1082 v->last_direction = v->direction;
1083 v->direction = newdir;
1084 }
1085
1086 /* Move vehicle. */
1087 gp = GetNewVehiclePos(v);
1088 } else {
1089 v->cur_speed >>= 1;
1090 v->direction = newdir;
1091
1092 /* When leaving a terminal an aircraft often goes to a position
1093 * directly in front of it. If it would move while turning it
1094 * would need an two extra turns to end up at the correct position.
1095 * To make it easier just disallow all moving while turning as
1096 * long as an aircraft is on the ground. */
1097 gp.x = v->x_pos;
1098 gp.y = v->y_pos;
1099 gp.new_tile = gp.old_tile = v->tile;
1100 }
1101 } else {
1103 /* Move vehicle. */
1104 gp = GetNewVehiclePos(v);
1105 }
1106 }
1107
1108 v->tile = gp.new_tile;
1109 /* If vehicle is in the air, use tile coordinate 0. */
1110 if (amd.flags.Any({AirportMovingDataFlag::Takeoff, AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::Land})) v->tile = TileIndex{};
1111
1112 /* Adjust Z for land or takeoff? */
1113 int z = v->z_pos;
1114
1116 z = GetAircraftFlightLevel(v, true);
1117 } else if (amd.flags.Test(AirportMovingDataFlag::Hold)) {
1118 /* Let the plane drop from normal flight altitude to holding pattern altitude */
1119 if (z > GetAircraftHoldMaxAltitude(v)) z--;
1120 } else if (amd.flags.All({AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::NoSpeedClamp})) {
1121 z = GetAircraftFlightLevel(v);
1122 }
1123
1124 /* NewGRF airports (like a rotated intercontinental from OpenGFX+Airports) can be non-rectangular
1125 * and their primary (north-most) tile does not have to be part of the airport.
1126 * As such, the height of the primary tile can be different from the rest of the airport.
1127 * Given we are landing/breaking, and as such are not a helicopter, we know that there has to be a hangar.
1128 * We also know that the airport itself has to be completely flat (otherwise it is not a valid airport).
1129 * Therefore, use the height of this hangar to calculate our z-value. */
1130 int airport_z = v->z_pos;
1131 if (amd.flags.Any({AirportMovingDataFlag::Land, AirportMovingDataFlag::Brake}) && st != nullptr) {
1132 assert(st->airport.HasHangar());
1133 TileIndex hangar_tile = st->airport.GetHangarTile(0);
1134 airport_z = GetTileMaxPixelZ(hangar_tile) + 1; // To avoid clashing with the shadow
1135 }
1136
1139 /* Zeppeliner blocked the runway, abort landing */
1140 v->state = FLYING;
1142 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
1143 v->pos = v->previous_pos;
1144 continue;
1145 }
1146
1147 if (st->airport.tile == INVALID_TILE) {
1148 /* Airport has been removed, abort the landing procedure */
1149 v->state = FLYING;
1152 /* get aircraft back on running altitude */
1153 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
1154 continue;
1155 }
1156
1157 /* We're not flying below our destination, right? */
1158 assert(airport_z <= z);
1159 int t = std::max(1U, dist - 4);
1160 int delta = z - airport_z;
1161
1162 /* Only start lowering when we're sufficiently close for a 1:1 glide */
1163 if (delta >= t) {
1164 z -= CeilDiv(z - airport_z, t);
1165 }
1166 if (z < airport_z) z = airport_z;
1167 }
1168
1169 /* We've landed. Decrease speed when we're reaching end of runway. */
1171
1172 if (z > airport_z) {
1173 z--;
1174 } else if (z < airport_z) {
1175 z++;
1176 }
1177
1178 }
1179
1180 SetAircraftPosition(v, gp.x, gp.y, z);
1181 } while (--count != 0);
1182 return false;
1183}
1184
1191{
1192 v->crashed_counter += 3;
1193
1195
1196 /* make aircraft crash down to the ground */
1197 if (v->crashed_counter < 500 && st == nullptr && ((v->crashed_counter % 3) == 0)) {
1198 int z = GetSlopePixelZ(Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE), Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE));
1199 v->z_pos -= 1;
1200 if (v->z_pos <= z) {
1201 v->crashed_counter = 500;
1202 v->z_pos = z + 1;
1203 } else {
1204 v->crashed_counter = 0;
1205 }
1206 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1207 }
1208
1209 if (v->crashed_counter < 650) {
1210 uint32_t r;
1211 if (Chance16R(1, 32, r)) {
1212 static const DirDiff delta[] = {
1214 };
1215
1216 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
1217 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1218 r = Random();
1220 GB(r, 0, 4) - 4,
1221 GB(r, 4, 4) - 4,
1222 GB(r, 8, 4),
1224 }
1225 } else if (v->crashed_counter >= 10000) {
1226 /* remove rubble of crashed airplane */
1227
1228 /* clear runway-in on all airports, set by crashing plane
1229 * small airports use AirportBlock::AirportBusy, city airports use AirportBlock::RunwayInOut, etc.
1230 * but they all share the same number */
1231 if (st != nullptr) {
1233 st->airport.blocks.Reset(AirportBlock::RunwayInOut); // commuter airport
1234 st->airport.blocks.Reset(AirportBlock::RunwayIn2); // intercontinental
1235 }
1236
1237 delete v;
1238
1239 return false;
1240 }
1241
1242 return true;
1243}
1244
1245
1251static void HandleAircraftSmoke(Aircraft *v, bool mode)
1252{
1253 static constexpr DirectionIndexArray<Coord2D<int8_t>> smoke_pos{{{
1254 { 5, 5},
1255 { 6, 0},
1256 { 5, -5},
1257 { 0, -6},
1258 {-5, -5},
1259 {-6, 0},
1260 {-5, 5},
1261 { 0, 6},
1262 }}};
1263
1264 if (!v->vehstatus.Test(VehState::AircraftBroken)) return;
1265
1266 /* Stop smoking when landed */
1267 if (v->cur_speed < 10) {
1269 v->breakdown_ctr = 0;
1270 return;
1271 }
1272
1273 /* Spawn effect et most once per Tick, i.e. !mode */
1274 if (!mode && (v->tick_counter & 0x0F) == 0) {
1276 smoke_pos[v->direction].x,
1277 smoke_pos[v->direction].y,
1278 2,
1280 );
1281 }
1282}
1283
1284void HandleMissingAircraftOrders(Aircraft *v)
1285{
1286 /*
1287 * We do not have an order. This can be divided into two cases:
1288 * 1) we are heading to an invalid station. In this case we must
1289 * find another airport to go to. If there is nowhere to go,
1290 * we will destroy the aircraft as it otherwise will enter
1291 * the holding pattern for the first airport, which can cause
1292 * the plane to go into an undefined state when building an
1293 * airport with the same StationID.
1294 * 2) we are (still) heading to a (still) valid airport, then we
1295 * can continue going there. This can happen when you are
1296 * changing the aircraft's orders while in-flight or in for
1297 * example a depot. However, when we have a current order to
1298 * go to a depot, we have to keep that order so the aircraft
1299 * actually stops.
1300 */
1301 const Station *st = GetTargetAirportIfValid(v);
1302 if (st == nullptr) {
1303 AutoRestoreBackup cur_company(_current_company, v->owner);
1304 CommandCost ret = Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag{}, {});
1305
1306 if (ret.Failed()) CrashAirplane(v);
1307 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
1308 v->current_order.Free();
1309 }
1310}
1311
1312
1314{
1315 /* Orders are changed in flight, ensure going to the right station. */
1316 if (this->state == FLYING) {
1318 }
1319
1320 /* Aircraft do not use dest-tile */
1321 return TileIndex{};
1322}
1323
1325{
1326 this->colourmap = PAL_NONE;
1327 this->UpdateViewport(true, false);
1328 if (this->subtype == AIR_HELICOPTER) {
1329 GetRotorImage(this, EngineImageType::OnMap, &this->Next()->Next()->sprite_cache.sprite_seq);
1330 }
1331}
1332
1333
1334uint Aircraft::Crash(bool flooded)
1335{
1336 uint victims = Vehicle::Crash(flooded) + 2; // pilots
1337 this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
1338
1339 return victims;
1340}
1341
1347{
1349
1350 uint victims = v->Crash();
1351
1352 v->cargo.Truncate();
1353 v->Next()->cargo.Truncate();
1354 const Station *st = GetTargetAirportIfValid(v);
1356
1357 EncodedString headline;
1358 if (st == nullptr) {
1359 headline = GetEncodedString(STR_NEWS_PLANE_CRASH_OUT_OF_FUEL, victims);
1360 } else {
1361 headline = GetEncodedString(STR_NEWS_AIRCRAFT_CRASH, victims, st->index);
1362 }
1363
1364 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner));
1365 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner));
1366
1367 NewsType newstype = NewsType::Accident;
1368 if (v->owner != _local_company) {
1369 newstype = NewsType::AccidentOther;
1370 }
1371
1372 AddTileNewsItem(std::move(headline), newstype, vt, st != nullptr ? st->index : StationID::Invalid());
1373
1374 ModifyStationRatingAround(vt, v->owner, -160, 30);
1375 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1376}
1377
1383{
1384
1386
1387 uint32_t prob;
1389 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
1390 !_cheats.no_jetcrash.value) {
1391 prob = 3276;
1392 } else {
1393 if (_settings_game.vehicle.plane_crashes == 0) return;
1394 prob = (0x4000 << _settings_game.vehicle.plane_crashes) / 1500;
1395 }
1396
1397 if (GB(Random(), 0, 22) > prob) return;
1398
1399 /* Crash the airplane. Remove all goods stored at the station. */
1400 for (GoodsEntry &ge : st->goods) {
1401 ge.rating = 1;
1402 if (ge.HasData()) ge.GetData().cargo.Truncate();
1403 }
1404
1405 CrashAirplane(v);
1406}
1407
1414{
1415 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
1416
1419
1420 /* Check if station was ever visited before */
1421 if (!st->had_vehicle_of_type.Test(StationVehicleType::Aircraft)) {
1422 st->had_vehicle_of_type.Set(StationVehicleType::Aircraft);
1423 /* show newsitem of celebrating citizens */
1425 GetEncodedString(STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, st->index),
1427 v->index,
1428 st->index
1429 );
1430 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
1431 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
1432 }
1433
1434 v->BeginLoading();
1435}
1436
1442{
1444
1445 TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1446
1447 v->UpdateDeltaXY();
1448
1449 TriggerAirportTileAnimation(st, vt, AirportAnimationTrigger::AirplaneTouchdown);
1450
1452 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
1453 }
1454}
1455
1456
1462{
1463 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
1464 v->targetairport = v->current_order.GetDestination().ToStationID();
1465 }
1466
1467 const Station *st = GetTargetAirportIfValid(v);
1468 const AirportFTAClass *apc = st == nullptr ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
1469 Direction rotation = st == nullptr ? Direction::N : st->airport.rotation;
1470 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
1471}
1472
1482{
1483 v->cur_speed = 0;
1484 v->subspeed = 0;
1485 v->progress = 0;
1486 v->direction = exit_dir;
1488 {
1489 Vehicle *u = v->Next();
1491
1492 /* Rotor blades */
1493 u = u->Next();
1494 if (u != nullptr) {
1496 u->cur_speed = 80;
1497 }
1498 }
1499
1502 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1503 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
1504 SetWindowClassesDirty(WindowClass::AircraftList);
1505}
1506
1510
1513{
1515 v->state = apc->layout[v->pos].heading;
1516}
1517
1520{
1522 v->state = apc->layout[v->pos].heading;
1523}
1524
1527{
1528 /* if we just arrived, execute EnterHangar first */
1529 if (v->previous_pos != v->pos) {
1531 return;
1532 }
1533
1534 /* if we were sent to the depot, stay there */
1535 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->vehstatus.Test(VehState::Stopped)) {
1536 v->current_order.Free();
1537 return;
1538 }
1539
1540 /* Check if we should wait here for unbunching. */
1541 if (v->IsWaitingForUnbunching()) return;
1542
1543 if (!v->current_order.IsType(OT_GOTO_STATION) &&
1544 !v->current_order.IsType(OT_GOTO_DEPOT))
1545 return;
1546
1547 /* We are leaving a hangar, but have to go to the exact same one; re-enter */
1548 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
1550 return;
1551 }
1552
1553 /* if the block of the next position is busy, stay put */
1554 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1555
1556 /* We are already at the target airport, we need to find a terminal */
1558 /* FindFreeTerminal:
1559 * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
1560 if (v->subtype == AIR_HELICOPTER) {
1561 if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
1562 } else {
1563 if (!AirportFindFreeTerminal(v, apc)) return; // airplane
1564 }
1565 } else { // Else prepare for launch.
1566 /* airplane goto state takeoff, helicopter to helitakeoff */
1568 }
1569 const Station *st = Station::GetByTile(v->tile);
1571 AirportMove(v, apc);
1572}
1573
1576{
1577 /* if we just arrived, execute EnterTerminal first */
1578 if (v->previous_pos != v->pos) {
1580 /* on an airport with helipads, a helicopter will always land there
1581 * and get serviced at the same time - setting */
1582 if (_settings_game.order.serviceathelipad) {
1583 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
1584 /* an excerpt of ServiceAircraft, without the invisibility stuff */
1589 SetWindowDirty(WindowClass::VehicleDetails, v->index);
1590 }
1591 }
1592 return;
1593 }
1594
1595 if (v->current_order.IsType(OT_NOTHING)) return;
1596
1597 /* if the block of the next position is busy, stay put */
1598 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1599
1600 /* airport-road is free. We either have to go to another airport, or to the hangar
1601 * ---> start moving */
1602
1603 bool go_to_hangar = false;
1604 switch (v->current_order.GetType()) {
1605 case OT_GOTO_STATION: // ready to fly to another airport
1606 break;
1607 case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc.
1608 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
1609 break;
1610 case OT_CONDITIONAL:
1611 /* In case of a conditional order we just have to wait a tick
1612 * longer, so the conditional order can actually be processed;
1613 * we should not clear the order as that makes us go nowhere. */
1614 return;
1615 default: // orders have been deleted (no orders), goto depot and don't bother us
1616 v->current_order.Free();
1617 go_to_hangar = true;
1618 }
1619
1620 if (go_to_hangar && Station::Get(v->targetairport)->airport.HasHangar()) {
1621 v->state = HANGAR;
1622 } else {
1623 /* airplane goto state takeoff, helicopter to helitakeoff */
1625 }
1626 AirportMove(v, apc);
1627}
1628
1630static void AircraftEventHandler_TakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1631{
1632 PlayAircraftSound(v); // play takeoffsound for airplanes
1633 v->state = STARTTAKEOFF;
1634}
1635
1637static void AircraftEventHandler_StartTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1638{
1639 v->state = ENDTAKEOFF;
1640 v->UpdateDeltaXY();
1641}
1642
1644static void AircraftEventHandler_EndTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1645{
1646 v->state = FLYING;
1647 /* get the next position to go to, differs per airport */
1649}
1650
1652static void AircraftEventHandler_HeliTakeOff(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1653{
1654 v->state = FLYING;
1655 v->UpdateDeltaXY();
1656
1657 /* get the next position to go to, differs per airport */
1659
1660 /* Send the helicopter to a hangar if needed for replacement */
1661 if (v->NeedsAutomaticServicing()) {
1662 AutoRestoreBackup cur_company(_current_company, v->owner);
1663 Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
1664 }
1665}
1666
1669{
1671
1672 /* Runway busy, not allowed to use this airstation or closed, circle. */
1673 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !st->airport.blocks.Test(AirportBlock::AirportClosed)) {
1674 /* {32,FLYING,AirportBlock::Nothing,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
1675 * if it is an airplane, look for LANDING, for helicopter HELILANDING
1676 * it is possible to choose from multiple landing runways, so loop until a free one is found */
1677 uint8_t landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
1678 const AirportFTA *current = apc->layout[v->pos].next.get();
1679 while (current != nullptr) {
1680 if (current->heading == landingtype) {
1681 /* save speed before, since if AirportHasBlock is false, it resets them to 0
1682 * we don't want that for plane in air
1683 * hack for speed thingy */
1684 uint16_t tcur_speed = v->cur_speed;
1685 uint16_t tsubspeed = v->subspeed;
1686 if (!AirportHasBlock(v, current, apc)) {
1687 v->state = landingtype; // LANDING / HELILANDING
1689 /* it's a bit dirty, but I need to set position to next position, otherwise
1690 * if there are multiple runways, plane won't know which one it took (because
1691 * they all have heading LANDING). And also occupy that block! */
1692 v->pos = current->next_position;
1693 st->airport.blocks.Set(apc->layout[v->pos].blocks);
1694 return;
1695 }
1696 v->cur_speed = tcur_speed;
1697 v->subspeed = tsubspeed;
1698 }
1699 current = current->next.get();
1700 }
1701 }
1702 v->state = FLYING;
1703 v->pos = apc->layout[v->pos].next_position;
1704}
1705
1707static void AircraftEventHandler_Landing(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1708{
1709 v->state = ENDLANDING;
1710 AircraftLandAirplane(v); // maybe crash airplane
1711
1712 /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
1713 if (v->NeedsAutomaticServicing()) {
1714 AutoRestoreBackup cur_company(_current_company, v->owner);
1715 Command<Commands::SendVehicleToDepot>::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {});
1716 }
1717}
1718
1720static void AircraftEventHandler_HeliLanding(Aircraft *v, [[maybe_unused]] const AirportFTAClass *apc)
1721{
1722 v->state = HELIENDLANDING;
1723 v->UpdateDeltaXY();
1724}
1725
1728{
1729 /* next block busy, don't do a thing, just wait */
1730 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1731
1732 /* if going to terminal (OT_GOTO_STATION) choose one
1733 * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
1734 * 2. not going for terminal (but depot, no order),
1735 * --> get out of the way to the hangar. */
1736 if (v->current_order.IsType(OT_GOTO_STATION)) {
1737 if (AirportFindFreeTerminal(v, apc)) return;
1738 }
1739 v->state = HANGAR;
1740}
1741
1744{
1745 /* next block busy, don't do a thing, just wait */
1746 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1747
1748 /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
1749 * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
1750 * 2. not going for terminal (but depot, no order),
1751 * --> get out of the way to the hangar IF there are terminals on the airport.
1752 * --> else TAKEOFF
1753 * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
1754 * must go to a hangar. */
1755 if (v->current_order.IsType(OT_GOTO_STATION)) {
1756 if (AirportFindFreeHelipad(v, apc)) return;
1757 }
1759}
1760
1766using AircraftStateHandler = void(Aircraft *v, const AirportFTAClass *apc);
1767
1770 [](Aircraft *, const AirportFTAClass *) { NOT_REACHED(); }, // TO_ALL = 0
1771 AircraftEventHandler_InHangar, // HANGAR = 1
1778 AircraftEventHandler_AtTerminal, // HELIPAD1 = 8
1779 AircraftEventHandler_AtTerminal, // HELIPAD2 = 9
1780 AircraftEventHandler_TakeOff, // TAKEOFF = 10
1781 AircraftEventHandler_StartTakeOff, // STARTTAKEOFF = 11
1782 AircraftEventHandler_EndTakeOff, // ENDTAKEOFF = 12
1783 AircraftEventHandler_HeliTakeOff, // HELITAKEOFF = 13
1784 AircraftEventHandler_Flying, // FLYING = 14
1785 AircraftEventHandler_Landing, // LANDING = 15
1786 AircraftEventHandler_EndLanding, // ENDLANDING = 16
1787 AircraftEventHandler_HeliLanding, // HELILANDING = 17
1788 AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
1789 AircraftEventHandler_AtTerminal, // TERM7 = 19
1790 AircraftEventHandler_AtTerminal, // TERM8 = 20
1791 AircraftEventHandler_AtTerminal, // HELIPAD3 = 21
1792};
1793
1794static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
1795{
1796 /* we have left the previous block, and entered the new one. Free the previous block */
1797 if (apc->layout[v->previous_pos].blocks != apc->layout[v->pos].blocks) {
1799
1801 apc->layout[v->previous_pos].blocks == AirportBlock::RunwayIn) {
1802 return;
1803 }
1804
1805 st->airport.blocks.Reset(apc->layout[v->previous_pos].blocks);
1806 }
1807}
1808
1809static void AirportGoToNextPosition(Aircraft *v)
1810{
1811 /* if aircraft is not in position, wait until it is */
1812 if (!AircraftController(v)) return;
1813
1815
1816 AirportClearBlock(v, apc);
1817 AirportMove(v, apc); // move aircraft to next position
1818}
1819
1820/* gets pos from vehicle and next orders */
1821static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
1822{
1823 /* error handling */
1824 if (v->pos >= apc->nofelements) {
1825 Debug(misc, 0, "[Ap] position {} is not valid for current airport. Max position is {}", v->pos, apc->nofelements - 1);
1826 assert(v->pos < apc->nofelements);
1827 }
1828
1829 const AirportFTA *current = &apc->layout[v->pos];
1830 /* we have arrived in an important state (eg terminal, hangar, etc.) */
1831 if (current->heading == v->state) {
1832 uint8_t prev_pos = v->pos; // location could be changed in state, so save it before-hand
1833 uint8_t prev_state = v->state;
1834 _aircraft_state_handlers[v->state](v, apc);
1835 if (v->state != FLYING) v->previous_pos = prev_pos;
1836 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
1837 return true;
1838 }
1839
1840 v->previous_pos = v->pos; // save previous location
1841
1842 /* there is only one choice to move to */
1843 if (current->next == nullptr) {
1844 if (AirportSetBlocks(v, current, apc)) {
1845 v->pos = current->next_position;
1847 } // move to next position
1848 return false;
1849 }
1850
1851 /* there are more choices to choose from, choose the one that
1852 * matches our heading */
1853 do {
1854 if (v->state == current->heading || current->heading == TO_ALL) {
1855 if (AirportSetBlocks(v, current, apc)) {
1856 v->pos = current->next_position;
1858 } // move to next position
1859 return false;
1860 }
1861 current = current->next.get();
1862 } while (current != nullptr);
1863
1864 Debug(misc, 0, "[Ap] cannot move further on Airport! (pos {} state {}) for vehicle {}", v->pos, v->state, v->index);
1865 NOT_REACHED();
1866}
1867
1875static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1876{
1877 const AirportFTA *reference = &apc->layout[v->pos];
1878 const AirportFTA *next = &apc->layout[current_pos->next_position];
1879
1880 /* same block, then of course we can move */
1881 if (apc->layout[current_pos->position].blocks != next->blocks) {
1882 const Station *st = Station::Get(v->targetairport);
1883 AirportBlocks blocks = next->blocks;
1884
1885 /* check additional possible extra blocks */
1886 if (current_pos != reference && current_pos->blocks != AirportBlock::Nothing) {
1887 blocks.Set(current_pos->blocks);
1888 }
1889
1890 if (st->airport.blocks.Any(blocks)) {
1891 v->cur_speed = 0;
1892 v->subspeed = 0;
1893 return true;
1894 }
1895 }
1896 return false;
1897}
1898
1906static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1907{
1908 const AirportFTA *next = &apc->layout[current_pos->next_position];
1909 const AirportFTA *reference = &apc->layout[v->pos];
1910
1911 /* if the next position is in another block, check it and wait until it is free */
1912 if (!apc->layout[current_pos->position].blocks.All(next->blocks)) {
1913 AirportBlocks blocks = next->blocks;
1914 /* search for all all elements in the list with the same state, and blocks != N
1915 * this means more blocks should be checked/set */
1916 const AirportFTA *current = current_pos;
1917 if (current == reference) current = current->next.get();
1918 while (current != nullptr) {
1919 if (current->heading == current_pos->heading && current->blocks.Any()) {
1920 blocks.Set(current->blocks);
1921 break;
1922 }
1923 current = current->next.get();
1924 }
1925
1926 /* if the block to be checked is in the next position, then exclude that from
1927 * checking, because it has been set by the airplane before */
1928 if (current_pos->blocks == next->blocks) blocks.Flip(next->blocks);
1929
1931 if (st->airport.blocks.Any(blocks)) {
1932 v->cur_speed = 0;
1933 v->subspeed = 0;
1934 return false;
1935 }
1936
1937 if (next->blocks != AirportBlock::Nothing) {
1938 st->airport.blocks.Set(blocks); // occupy next block
1939 }
1940 }
1941 return true;
1942}
1943
1952
1967
1975static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal)
1976{
1977 assert(last_terminal <= lengthof(_airport_terminal_mapping));
1979 for (; i < last_terminal; i++) {
1980 if (!st->airport.blocks.Any(_airport_terminal_mapping[i].blocks)) {
1981 /* TERMINAL# HELIPAD# */
1982 v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
1983 st->airport.blocks.Set(_airport_terminal_mapping[i].blocks); // occupy terminal/helipad
1984 return true;
1985 }
1986 }
1987 return false;
1988}
1989
1995static uint GetNumTerminals(const AirportFTAClass *apc)
1996{
1997 uint num = 0;
1998
1999 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
2000
2001 return num;
2002}
2003
2011{
2012 /* example of more terminalgroups
2013 * {0,HANGAR,AirportBlock::Nothing,1}, {0,TERMGROUP,AirportBlock::TermGroup1,0}, {0,TERMGROUP,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
2014 * Heading TERMGROUP denotes a group. We see 2 groups here:
2015 * 1. group 0 -- AirportBlock::TermGroup1 (check block)
2016 * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
2017 * First in line is checked first, group 0. If the block (AirportBlock::TermGroup1) is free, it
2018 * looks at the corresponding terminals of that group. If no free ones are found, other
2019 * possible groups are checked (in this case group 1, since that is after group 0). If that
2020 * fails, then attempt fails and plane waits
2021 */
2022 if (apc->terminals[0] > 1) {
2023 const Station *st = Station::Get(v->targetairport);
2024 const AirportFTA *temp = apc->layout[v->pos].next.get();
2025
2026 while (temp != nullptr) {
2027 if (temp->heading == TERMGROUP) {
2028 if (!st->airport.blocks.Any(temp->blocks)) {
2029 /* read which group do we want to go to?
2030 * (the first free group) */
2031 uint target_group = temp->next_position + 1;
2032
2033 /* at what terminal does the group start?
2034 * that means, sum up all terminals of
2035 * groups with lower number */
2036 uint group_start = 0;
2037 for (uint i = 1; i < target_group; i++) {
2038 group_start += apc->terminals[i];
2039 }
2040
2041 uint group_end = group_start + apc->terminals[target_group];
2042 if (FreeTerminal(v, group_start, group_end)) return true;
2043 }
2044 } else {
2045 /* once the heading isn't 255, we've exhausted the possible blocks.
2046 * So we cannot move */
2047 return false;
2048 }
2049 temp = temp->next.get();
2050 }
2051 }
2052
2053 /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
2054 return FreeTerminal(v, 0, GetNumTerminals(apc));
2055}
2056
2064{
2065 /* if an airport doesn't have helipads, use terminals */
2066 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
2067
2068 /* only 1 helicoptergroup, check all helipads
2069 * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
2071}
2072
2078static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
2079{
2080 if (too_far) {
2083 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
2084 AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
2085 if (v->owner == _local_company) {
2086 /* Post a news message. */
2087 AddVehicleAdviceNewsItem(AdviceType::AircraftDestinationTooFar, GetEncodedString(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index), v->index);
2088 }
2089 }
2090 return;
2091 }
2092
2094 /* Not too far anymore, clear flag and message. */
2096 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
2098 }
2099}
2100
2107static bool AircraftEventHandler(Aircraft *v, int loop)
2108{
2110 return HandleCrashedAircraft(v);
2111 }
2112
2113 if (v->vehstatus.Test(VehState::Stopped)) return true;
2114
2115 v->HandleBreakdown();
2116
2117 HandleAircraftSmoke(v, loop != 0);
2118 ProcessOrders(v);
2119 v->HandleLoading(loop != 0);
2120
2121 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
2122
2123 if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
2124 /* If we are flying, unconditionally clear the 'dest too far' state. */
2125 AircraftHandleDestTooFar(v, false);
2126 } else if (v->acache.cached_max_range_sqr != 0) {
2127 /* Check the distance to the next destination. This code works because the target
2128 * airport is only updated after take off and not on the ground. */
2130 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;
2131
2132 if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) {
2133 uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
2135 }
2136 }
2137
2138 if (!v->flags.Test(VehicleAirFlag::DestinationTooFar)) AirportGoToNextPosition(v);
2139
2140 return true;
2141}
2142
2144{
2145 if (!this->IsNormalAircraft()) return true;
2146
2148
2149 this->tick_counter++;
2150
2151 if (!this->vehstatus.Test(VehState::Stopped)) this->running_ticks++;
2152
2153 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
2154
2155 this->current_order_time++;
2156
2157 for (uint i = 0; i != 2; i++) {
2158 /* stop if the aircraft was deleted */
2159 if (!AircraftEventHandler(this, i)) return false;
2160 }
2161
2162 return true;
2163}
2164
2165
2173{
2174 assert(v->type == VehicleType::Aircraft);
2175
2177 if (st == nullptr) return nullptr;
2178
2179 return st->airport.tile == INVALID_TILE ? nullptr : st;
2180}
2181
2187{
2188 /* only 1 station is updated per function call, so it is enough to get entry_point once */
2189 const AirportFTAClass *ap = st->airport.GetFTA();
2191
2192 for (Aircraft *v : Aircraft::Iterate()) {
2193 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
2194 assert(v->state == FLYING);
2195
2196 Order *o = &v->current_order;
2197 /* The aircraft is heading to a hangar, but the new station doesn't have one,
2198 * or the aircraft can't land on the new station. Cancel current order. */
2199 if (o->IsType(OT_GOTO_DEPOT) && !o->GetDepotOrderType().Test(OrderDepotTypeFlag::PartOfOrders) && o->GetDestination() == st->index &&
2200 (!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
2201 o->MakeDummy();
2202 SetWindowWidgetDirty(WindowClass::VehicleView, v->index, WID_VV_START_STOP);
2203 }
2204 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
2206 }
2207
2208 /* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */
2209 if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true);
2210}
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:51
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.
@ HelicopterDirectDescent
The helicopter is descending directly at its destination (helipad or in front of hangar).
Definition aircraft.h:45
@ InMinimumHeightCorrection
The vehicle is currently raising its altitude because it hit the lower bound.
Definition aircraft.h:43
@ DestinationTooFar
Next destination is too far away.
Definition aircraft.h:37
@ InMaximumHeightCorrection
The vehicle is currently lowering its altitude because it hit the upper bound.
Definition aircraft.h:42
Station * GetTargetAirportIfValid(const Aircraft *v)
Returns aircraft's target station if v->target_airport is a valid station with airport.
@ 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 AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
Aircraft has taken off.
static void HandleAircraftSmoke(Aircraft *v, bool mode)
Handle smoke of broken aircraft.
static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
Helicopter is starting to land.
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 void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
Aircraft is has landed.
static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
Aircraft is landing (touchdown).
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.
void(Aircraft *v, const AirportFTAClass *apc) AircraftStateHandler
Signature of the aircraft handler function.
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)
Checks whether the next block the aircraft wants to travel on is busy.
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 AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
Helicopter has landed.
static void AircraftLandAirplane(Aircraft *v)
Aircraft touched down at the landing strip.
bool IsValidImageIndex< VehicleType::Aircraft >(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
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 bool AircraftEventHandler(Aircraft *v, int loop)
Event handler loop for a single aircraft.
static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
Aircraft is taking off (rolling).
static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
Helicopter takes off.
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.
static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
Aircraft is flying around.
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 void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
Aircraft is taking off (rotation).
static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
Aircraft arrives at the terminal.
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.
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:199
const AirportFTAClass * GetAirport(const uint8_t airport_type)
Get the finite state machine of an airport type.
Definition airport.cpp:188
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:91
@ Zeppeliner
Block for the zeppeliner disaster vehicle.
Definition airport.h:130
@ Term2
Block belonging to terminal 2.
Definition airport.h:93
@ RunwayIn
Runway used for landing (metropolitan / international / intercontinental airports).
Definition airport.h:101
@ AirportClosed
Dummy block for indicating a closed airport.
Definition airport.h:131
@ Term5
Block belonging to terminal 5.
Definition airport.h:96
@ Term6
Block belonging to terminal 6.
Definition airport.h:97
@ Term4
Block belonging to terminal 4.
Definition airport.h:95
@ Helipad3
Block belonging to helipad 3.
Definition airport.h:120
@ Term7
Block belonging to terminal 7.
Definition airport.h:118
@ Helipad1
Block belonging to helipad 1.
Definition airport.h:98
@ Term3
Block belonging to terminal 3.
Definition airport.h:94
@ RunwayInOut
Runway used for landing and take-off (commuter / city airports).
Definition airport.h:100
@ Term1
Block belonging to terminal 1.
Definition airport.h:92
@ RunwayIn2
Second runway for landing.
Definition airport.h:124
@ Helipad2
Block belonging to helipad 2.
Definition airport.h:99
@ Term8
Block belonging to terminal 8.
Definition airport.h:119
@ Nothing
Nothing is blocked, for example being in the hanger.
Definition airport.h:129
@ HeliRaise
Helicopter take-off.
Definition airport.h:54
@ HeliLower
Helicopter landing.
Definition airport.h:55
@ Land
Landing onto landing strip.
Definition airport.h:51
@ Takeoff
Takeoff movement.
Definition airport.h:49
@ SlowTurn
Turn slowly (mostly used in the air).
Definition airport.h:50
@ Brake
Taxiing at the airport.
Definition airport.h:53
@ NoSpeedClamp
No speed restrictions.
Definition airport.h:48
@ Hold
Holding pattern movement (above the airport).
Definition airport.h:56
@ ExactPosition
Go exactly to the destination coordinates.
Definition airport.h:52
EnumBitSet< AirportBlock, uint64_t > AirportBlocks
Bitset of AirportBlock elements.
Definition airport.h:135
AirportMovementStates
Movement States on Airports (headings target).
Definition airport.h:63
@ HELITAKEOFF
Helicopter wants to leave the airport.
Definition airport.h:77
@ TERM4
Heading for terminal 4.
Definition airport.h:69
@ STARTTAKEOFF
Airplane has arrived at a runway for take-off.
Definition airport.h:75
@ HELIPAD2
Heading for helipad 2.
Definition airport.h:73
@ ENDTAKEOFF
Airplane has reached end-point of the take-off runway.
Definition airport.h:76
@ TERM5
Heading for terminal 5.
Definition airport.h:70
@ TERM6
Heading for terminal 6.
Definition airport.h:71
@ TERM3
Heading for terminal 3.
Definition airport.h:68
@ TERM8
Heading for terminal 8.
Definition airport.h:84
@ HELIPAD3
Heading for helipad 3.
Definition airport.h:85
@ HELIPAD1
Heading for helipad 1.
Definition airport.h:72
@ TERM2
Heading for terminal 2.
Definition airport.h:67
@ HANGAR
Heading for hangar.
Definition airport.h:65
@ FLYING
Vehicle is flying in the air.
Definition airport.h:78
@ TAKEOFF
Airplane wants to leave the airport.
Definition airport.h:74
@ HELILANDING
Helicopter wants to land.
Definition airport.h:81
@ TO_ALL
Go in this direction for every target.
Definition airport.h:64
@ ENDLANDING
Airplane wants to finish landing.
Definition airport.h:80
@ HELIENDLANDING
Helicopter wants to finish landing.
Definition airport.h:82
@ TERM1
Heading for terminal 1.
Definition airport.h:66
@ LANDING
Airplane wants to land.
Definition airport.h:79
@ TERM7
Heading for terminal 7.
Definition airport.h:83
@ TERMGROUP
Aircraft is looking for a free terminal in a terminalgroup.
Definition airport.h:87
@ 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).
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
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:231
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 & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
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:52
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:227
EngineFlags flags
Flags of the engine.
Definition engine_base.h:59
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition engine_base.h:63
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:469
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:96
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:51
static void NewEvent(class ScriptEvent *event)
Queue a new event for the game script.
RAII class for measuring multi-step elements of performance.
uint Truncate(uint max_move=UINT_MAX, StationCargoAmountMap *cargo_per_source=nullptr)
Truncates where each destination loses roughly the same percentage of its cargo.
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 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
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
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.
DirDiff LimitDirDiff(DirDiff d)
Limit a direction difference to up to 45 degrees.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
DirDiff
Enumeration for the difference between two directions.
@ Left45
Angle of 45 degrees left.
@ Same
Both directions faces to the same direction.
@ Right45
Angle of 45 degrees right.
Direction
Defines the 8 directions on the map.
@ NE
Northeast.
@ SE
Southeast.
EnumIndexArray< T, Direction, Direction::End > DirectionIndexArray
Array with Direction as index.
DiagDirection
Enumeration for diagonal directions.
@ SW
Southwest.
@ NW
Northwest.
@ NE
Northeast, upper right on your monitor.
@ SE
Southeast.
All disaster vehicles.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:937
@ AircraftRun
Running costs aircraft.
@ RunningAircraft
Running cost of aircrafts.
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.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
#define T
Climate temperate.
Definition engines.h:91
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Error reporting related functions.
fluid_settings_t * settings
FluidSynth settings handle.
Types for recording game performance data.
@ GameLoopAircraft
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.
#define Rect
Macro that prevents name conflicts between included headers.
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition map.cpp:186
TileIndex TileVirtXYClampedToMap(int x, int y)
Get a tile from the virtual XY-coordinate.
Definition map.cpp:84
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:407
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
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_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:32
void AddVehicleAdviceNewsItem(AdviceType advice_type, EncodedString &&headline, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition news_func.h:43
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.
Definition news_type.h:30
@ AccidentOther
An accident or disaster has occurred.
Definition news_type.h:33
@ ArrivalOther
First vehicle arrived for competitor.
Definition news_type.h:31
@ Accident
An accident or disaster has occurred.
Definition news_type.h:32
@ AircraftDestinationTooFar
Next (order) destination is too far for the aircraft type.
Definition news_type.h:52
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.
@ NearestDepot
Send the vehicle to the nearest depot.
Definition order_type.h:121
@ PartOfOrders
This depot order is because of a regular order.
Definition order_type.h:110
@ Service
This depot order is because of the servicing limit.
Definition order_type.h:109
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:125
@ SND_18_TAKEOFF_HELICOPTER
22 == 0x16 Takeoff: helicopter
Definition sound_type.h:70
@ SND_17_SKID_PLANE
21 == 0x15 Plane landing / touching ground
Definition sound_type.h:69
@ SND_12_EXPLOSION
16 == 0x10 Destruction, crashes, disasters, ...
Definition sound_type.h:64
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1793
static const SpriteID SPR_ROTOR_STOPPED
Definition sprites.h:1106
static const SpriteID SPR_IMG_QUERY
Definition sprites.h:1274
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
@ Airport
Station with an airport.
@ AirplaneTouchdown
Triggered when an airplane (not a helicopter) touches down at the airport (for single tile).
@ Aircraft
Station has seen an aircraft.
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:261
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:68
uint16_t cached_max_range
Cached maximum range.
Definition aircraft.h:69
Information about a aircraft vehicle.
uint8_t mail_capacity
Mail capacity (bags).
uint8_t subtype
Type of aircraft.
uint16_t passenger_capacity
Passenger capacity (persons).
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition aircraft.h:75
bool Tick() override
Calls the tick handler of the vehicle.
uint8_t pos
Next desired position of the aircraft.
Definition aircraft.h:77
uint8_t state
State of the airport.
Definition aircraft.h:80
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:82
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:83
TileIndex GetOrderStationLocation(StationID station) override
Determine the location for the station where the vehicle goes to next.
VehicleAirFlags flags
Aircraft flags.
Definition aircraft.h:84
bool IsNormalAircraft() const
Check if the aircraft type is a normal flying device; eg not a rotor or a shadow.
Definition aircraft.h:123
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:78
StationID targetairport
Airport to go to next.
Definition aircraft.h:79
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:76
Finite sTate mAchine (FTA) of an airport.
Definition airport.h:161
const uint8_t num_helipads
Number of helipads on this airport. When 0 helicopters will go to normal terminals.
Definition airport.h:197
const AirportMovingData * MovingData(uint8_t position) const
Get movement data at a position.
Definition airport.h:188
@ Airplanes
Can planes land on this airport type?
Definition airport.h:165
@ ShortStrip
This airport has a short landing strip, dangerous for fast aircraft.
Definition airport.h:167
uint8_t delta_z
Z adjustment for helicopter pads.
Definition airport.h:201
std::vector< AirportFTA > layout
state machine for airport
Definition airport.h:195
const uint8_t * terminals
Array with the number of terminal groups, followed by the number of terminals in each group.
Definition airport.h:196
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:200
uint8_t nofelements
number of positions the airport consists of
Definition airport.h:199
Flags flags
Flags for this airport type.
Definition airport.h:198
Internal structure used in openttd - Finite sTate mAchine --> FTA.
Definition airport.h:150
std::unique_ptr< AirportFTA > next
possible extra movement choices from this position
Definition airport.h:153
uint8_t heading
heading (current orders), guiding an airplane to its target on an airport
Definition airport.h:157
AirportBlocks blocks
bitmap of blocks that could be reserved
Definition airport.h:154
uint8_t position
the position that an airplane is at
Definition airport.h:155
uint8_t next_position
next position from this position
Definition airport.h:156
A single location on an airport where aircraft can move to.
Definition airport.h:138
int16_t x
x-coordinate of the destination.
Definition airport.h:139
AirportMovingDataFlags flags
special flags when moving towards the destination.
Definition airport.h:141
int16_t y
y-coordinate of the destination.
Definition airport.h:140
Direction direction
Direction to turn the aircraft after reaching the destination.
Definition airport.h:142
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 upon destruction of this object to prevent stack v...
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.
Structure to return information about the closest depot location, and whether it could be found.
Disasters, like submarines, skyrangers and their shadows, belong to this class.
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.
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Stores station stats for a single cargo.
const GoodsEntryData & GetData() const
Get optional cargo packet/flow data.
uint8_t rating
Station rating for this cargo.
bool HasData() const
Test if this goods entry has optional cargo packet/flow data.
static uint MaxY()
Gets the maximum Y coordinate within the map, including TileType::Void.
Definition map_func.h:298
static uint MaxX()
Gets the maximum X coordinate within the map, including TileType::Void.
Definition map_func.h:289
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.
If you change this, keep in mind that it is also saved in 2 other places:
Definition order_base.h:34
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition order_base.h:170
DestinationID GetDestination() const
Gets the destination of this order.
Definition order_base.h:100
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
OrderType GetType() const
Get the type of order of this order.
Definition order_base.h:73
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:176
void Free()
'Free' the order
Definition order_cmd.cpp:48
void MakeGoToDepot(DestinationID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=OrderNonStopFlag::NonStop, OrderDepotActionFlags action={}, 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< Vehicle > Iterate(size_t from=0)
static T * Create(Targs &&... args)
static Engine * Get(auto index)
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
static Station * Get(auto index)
static Station * GetIfValid(auto index)
T * Next() const
Get next vehicle in the chain.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
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).
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:124
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:152
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:749
virtual uint Crash(bool flooded=false)
Crash the (whole) vehicle chain.
Definition vehicle.cpp:301
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:2453
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:2533
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:2228
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:2580
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2985
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:1375
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:1783
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:1700
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:293
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
static uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
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:1563
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition vehicle.cpp:188
void CheckVehicleBreakdown(Vehicle *v)
Periodic check for a vehicle to maybe break down.
Definition vehicle.cpp:1319
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1803
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition vehicle.cpp:1298
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition vehicle.cpp:1441
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition vehicle.cpp:3112
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition vehicle.cpp:1453
@ 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.
bool IsValidImageIndex(uint8_t image_index)
Helper to check whether an image index is valid for a particular vehicle.
EngineImageType
Visualisation contexts of vehicles and engines.
@ OnMap
Vehicle drawn in viewport.
@ Aircraft
Aircraft vehicle type.
DepotCommandFlag
Flags for goto depot commands.
@ Service
The vehicle will leave the depot right after arrival (service only).
Types related to the vehicle widgets.
@ 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:3226
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:3318
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:3212
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3196
Window functions not directly related to making/drawing windows.
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