OpenTTD Source  20241120-master-g6d3adc6169
aircraft_cmd.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
13 #include "stdafx.h"
14 #include "aircraft.h"
15 #include "landscape.h"
16 #include "news_func.h"
17 #include "newgrf_engine.h"
18 #include "newgrf_sound.h"
19 #include "spritecache.h"
20 #include "error_func.h"
21 #include "strings_func.h"
22 #include "command_func.h"
23 #include "window_func.h"
26 #include "vehicle_func.h"
27 #include "sound_func.h"
28 #include "cheat_type.h"
29 #include "company_base.h"
30 #include "ai/ai.hpp"
31 #include "game/game.hpp"
32 #include "company_func.h"
33 #include "effectvehicle_func.h"
34 #include "station_base.h"
35 #include "engine_base.h"
36 #include "core/random_func.hpp"
37 #include "core/backup_type.hpp"
38 #include "zoom_func.h"
39 #include "disaster_vehicle.h"
40 #include "newgrf_airporttiles.h"
41 #include "framerate_type.h"
42 #include "aircraft_cmd.h"
43 #include "vehicle_cmd.h"
44 
45 #include "table/strings.h"
46 
47 #include "safeguards.h"
48 
50 {
51  this->x_offs = -1;
52  this->y_offs = -1;
53  this->x_extent = 2;
54  this->y_extent = 2;
55 
56  switch (this->subtype) {
57  default: NOT_REACHED();
58 
59  case AIR_AIRCRAFT:
60  case AIR_HELICOPTER:
61  switch (this->state) {
62  default: break;
63  case ENDTAKEOFF:
64  case LANDING:
65  case HELILANDING:
66  case FLYING:
67  this->x_extent = 24;
68  this->y_extent = 24;
69  break;
70  }
71  this->z_extent = 5;
72  break;
73 
74  case AIR_SHADOW:
75  this->z_extent = 1;
76  this->x_offs = 0;
77  this->y_offs = 0;
78  break;
79 
80  case AIR_ROTOR:
81  this->z_extent = 1;
82  break;
83  }
84 }
85 
86 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
87 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
88 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
89 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
90 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
91 static void CrashAirplane(Aircraft *v);
92 
93 static const SpriteID _aircraft_sprite[] = {
94  0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
95  0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
96  0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
97  0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
98  0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
99  0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
100  0x0EBD, 0x0EC5
101 };
102 
103 template <>
104 bool IsValidImageIndex<VEH_AIRCRAFT>(uint8_t image_index)
105 {
106  return image_index < lengthof(_aircraft_sprite);
107 }
108 
111  HRS_ROTOR_STOPPED,
112  HRS_ROTOR_MOVING_1,
113  HRS_ROTOR_MOVING_2,
114  HRS_ROTOR_MOVING_3,
115 };
116 
124 static StationID FindNearestHangar(const Aircraft *v)
125 {
126  uint best = 0;
127  StationID index = INVALID_STATION;
128  TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
129  const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
130  uint max_range = v->acache.cached_max_range_sqr;
131 
132  /* Determine destinations where it's coming from and where it's heading to */
133  const Station *last_dest = nullptr;
134  const Station *next_dest = nullptr;
135  if (max_range != 0) {
136  if (v->current_order.IsType(OT_GOTO_STATION) ||
137  (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) {
140  } else {
141  last_dest = GetTargetAirportIfValid(v);
143  }
144  }
145 
146  for (const Station *st : Station::Iterate()) {
147  if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT) || !st->airport.HasHangar()) continue;
148 
149  const AirportFTAClass *afc = st->airport.GetFTA();
150 
151  /* don't crash the plane if we know it can't land at the airport */
152  if ((afc->flags & AirportFTAClass::SHORT_STRIP) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue;
153 
154  /* the plane won't land at any helicopter station */
155  if (!(afc->flags & AirportFTAClass::AIRPLANES) && (avi->subtype & AIR_CTOL)) continue;
156 
157  /* Check if our last and next destinations can be reached from the depot airport. */
158  if (max_range != 0) {
159  uint last_dist = (last_dest != nullptr && last_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, last_dest->airport.tile) : 0;
160  uint next_dist = (next_dest != nullptr && next_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, next_dest->airport.tile) : 0;
161  if (last_dist > max_range || next_dist > max_range) continue;
162  }
163 
164  /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
165  uint distance = DistanceSquare(vtile, st->airport.tile);
166  if (distance < best || index == INVALID_STATION) {
167  best = distance;
168  index = st->index;
169  }
170  }
171  return index;
172 }
173 
174 void Aircraft::GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const
175 {
176  uint8_t spritenum = this->spritenum;
177 
178  if (is_custom_sprite(spritenum)) {
179  GetCustomVehicleSprite(this, direction, image_type, result);
180  if (result->IsValid()) return;
181 
183  }
184 
185  assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
186  result->Set(direction + _aircraft_sprite[spritenum]);
187 }
188 
189 void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteSeq *result)
190 {
191  assert(v->subtype == AIR_HELICOPTER);
192 
193  const Aircraft *w = v->Next()->Next();
194  if (is_custom_sprite(v->spritenum)) {
195  GetCustomRotorSprite(v, image_type, result);
196  if (result->IsValid()) return;
197  }
198 
199  /* Return standard rotor sprites if there are no custom sprites for this helicopter */
200  result->Set(SPR_ROTOR_STOPPED + w->state);
201 }
202 
203 static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
204 {
205  const Engine *e = Engine::Get(engine);
206  uint8_t spritenum = e->u.air.image_index;
207 
208  if (is_custom_sprite(spritenum)) {
209  GetCustomVehicleIcon(engine, DIR_W, image_type, result);
210  if (result->IsValid()) return;
211 
212  spritenum = e->original_image_index;
213  }
214 
215  assert(IsValidImageIndex<VEH_AIRCRAFT>(spritenum));
216  result->Set(DIR_W + _aircraft_sprite[spritenum]);
217 }
218 
219 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
220 {
221  VehicleSpriteSeq seq;
222  GetAircraftIcon(engine, image_type, &seq);
223 
224  Rect rect;
225  seq.GetBounds(&rect);
226  preferred_x = Clamp(preferred_x,
227  left - UnScaleGUI(rect.left),
228  right - UnScaleGUI(rect.right));
229 
230  seq.Draw(preferred_x, y, pal, pal == PALETTE_CRASH);
231 
232  if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
233  VehicleSpriteSeq rotor_seq;
234  GetCustomRotorIcon(engine, image_type, &rotor_seq);
235  if (!rotor_seq.IsValid()) rotor_seq.Set(SPR_ROTOR_STOPPED);
236  rotor_seq.Draw(preferred_x, y - ScaleSpriteTrad(5), PAL_NONE, false);
237  }
238 }
239 
249 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
250 {
251  VehicleSpriteSeq seq;
252  GetAircraftIcon(engine, image_type, &seq);
253 
254  Rect rect;
255  seq.GetBounds(&rect);
256 
257  width = UnScaleGUI(rect.Width());
258  height = UnScaleGUI(rect.Height());
259  xoffs = UnScaleGUI(rect.left);
260  yoffs = UnScaleGUI(rect.top);
261 }
262 
272 {
273  const AircraftVehicleInfo *avi = &e->u.air;
274  const Station *st = Station::GetByTile(tile);
275 
276  /* Prevent building aircraft types at places which can't handle them */
277  if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
278 
279  /* Make sure all aircraft end up in the first tile of the hangar. */
280  tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
281 
282  if (flags & DC_EXEC) {
283  Aircraft *v = new Aircraft(); // aircraft
284  Aircraft *u = new Aircraft(); // shadow
285  *ret = v;
286 
287  v->direction = DIR_SE;
288 
289  v->owner = u->owner = _current_company;
290 
291  v->tile = tile;
292 
293  uint x = TileX(tile) * TILE_SIZE + 5;
294  uint y = TileY(tile) * TILE_SIZE + 3;
295 
296  v->x_pos = u->x_pos = x;
297  v->y_pos = u->y_pos = y;
298 
299  u->z_pos = GetSlopePixelZ(x, y);
300  v->z_pos = u->z_pos + 1;
301 
304 
305  v->spritenum = avi->image_index;
306 
307  v->cargo_cap = avi->passenger_capacity;
308  v->refit_cap = 0;
309  u->refit_cap = 0;
310 
312  assert(IsValidCargoID(v->cargo_type));
313 
314  CargoID mail = GetCargoIDByLabel(CT_MAIL);
315  if (IsValidCargoID(mail)) {
316  u->cargo_type = mail;
317  u->cargo_cap = avi->mail_capacity;
318  }
319 
320  v->name.clear();
321  v->last_station_visited = INVALID_STATION;
322  v->last_loading_station = INVALID_STATION;
323 
324  v->acceleration = avi->acceleration;
325  v->engine_type = e->index;
326  u->engine_type = e->index;
327 
329  v->UpdateDeltaXY();
330 
331  u->subtype = AIR_SHADOW;
332  u->UpdateDeltaXY();
333 
334  v->reliability = e->reliability;
336  v->max_age = e->GetLifeLengthInDays();
337 
338  v->pos = GetVehiclePosOnBuild(tile);
339 
340  v->state = HANGAR;
341  v->previous_pos = v->pos;
342  v->targetairport = GetStationIndex(tile);
343  v->SetNext(u);
344 
345  v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
346 
350 
351  v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
352  u->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY);
353 
354  v->random_bits = Random();
355  u->random_bits = Random();
356 
357  v->vehicle_flags = 0;
359  v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
360 
362 
363  v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
364 
366 
367  UpdateAircraftCache(v, true);
368 
369  v->UpdatePosition();
370  u->UpdatePosition();
371 
372  /* Aircraft with 3 vehicles (chopper)? */
373  if (v->subtype == AIR_HELICOPTER) {
374  Aircraft *w = new Aircraft();
375  w->engine_type = e->index;
376  w->direction = DIR_N;
377  w->owner = _current_company;
378  w->x_pos = v->x_pos;
379  w->y_pos = v->y_pos;
380  w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
382  w->spritenum = 0xFF;
383  w->subtype = AIR_ROTOR;
384  w->sprite_cache.sprite_seq.Set(SPR_ROTOR_STOPPED);
385  w->random_bits = Random();
386  /* Use rotor's air.state to store the rotor animation frame */
387  w->state = HRS_ROTOR_STOPPED;
388  w->UpdateDeltaXY();
389 
390  u->SetNext(w);
391  w->UpdatePosition();
392  }
393  }
394 
395  return CommandCost();
396 }
397 
398 
400 {
401  const Station *st = GetTargetAirportIfValid(this);
402  /* If the station is not a valid airport or if it has no hangars */
403  if (st == nullptr || !CanVehicleUseStation(this, st) || !st->airport.HasHangar()) {
404  /* the aircraft has to search for a hangar on its own */
405  StationID station = FindNearestHangar(this);
406 
407  if (station == INVALID_STATION) return ClosestDepot();
408 
409  st = Station::Get(station);
410  }
411 
412  return ClosestDepot(st->xy, st->index);
413 }
414 
415 static void CheckIfAircraftNeedsService(Aircraft *v)
416 {
417  if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
418  if (v->IsChainInDepot()) {
420  return;
421  }
422 
423  /* When we're parsing conditional orders and the like
424  * we don't want to consider going to a depot too. */
425  if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
426 
428 
429  assert(st != nullptr);
430 
431  /* only goto depot if the target airport has a depot */
432  if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
435  } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
438  }
439 }
440 
442 {
443  const Engine *e = this->GetEngine();
444  uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
445  return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
446 }
447 
450 {
451  if (!this->IsNormalAircraft()) return;
452  AgeVehicle(this);
453 }
454 
457 {
458  if (!this->IsNormalAircraft()) return;
459  EconomyAgeVehicle(this);
460 
461  if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
462 
463  CheckOrders(this);
464 
465  CheckVehicleBreakdown(this);
466  CheckIfAircraftNeedsService(this);
467 
468  if (this->running_ticks == 0) return;
469 
471 
472  this->profit_this_year -= cost.GetCost();
473  this->running_ticks = 0;
474 
476 
479 }
480 
481 static void HelicopterTickHandler(Aircraft *v)
482 {
483  Aircraft *u = v->Next()->Next();
484 
485  if (u->vehstatus & VS_HIDDEN) return;
486 
487  /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
488  * loading/unloading at a terminal or stopped */
489  if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
490  if (u->cur_speed != 0) {
491  u->cur_speed++;
492  if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
493  u->cur_speed = 0;
494  }
495  }
496  } else {
497  if (u->cur_speed == 0) {
498  u->cur_speed = 0x70;
499  }
500  if (u->cur_speed >= 0x50) {
501  u->cur_speed--;
502  }
503  }
504 
505  int tick = ++u->tick_counter;
506  int spd = u->cur_speed >> 4;
507 
508  VehicleSpriteSeq seq;
509  if (spd == 0) {
510  u->state = HRS_ROTOR_STOPPED;
511  GetRotorImage(v, EIT_ON_MAP, &seq);
512  if (u->sprite_cache.sprite_seq == seq) return;
513  } else if (tick >= spd) {
514  u->tick_counter = 0;
515  u->state++;
516  if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
517  GetRotorImage(v, EIT_ON_MAP, &seq);
518  } else {
519  return;
520  }
521 
522  u->sprite_cache.sprite_seq = seq;
523 
525 }
526 
534 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
535 {
536  v->x_pos = x;
537  v->y_pos = y;
538  v->z_pos = z;
539 
540  v->UpdatePosition();
541  v->UpdateViewport(true, false);
542  if (v->subtype == AIR_HELICOPTER) {
543  GetRotorImage(v, EIT_ON_MAP, &v->Next()->Next()->sprite_cache.sprite_seq);
544  }
545 
546  Aircraft *u = v->Next();
547 
548  int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
549  int safe_y = Clamp(y - 1, 0, Map::MaxY() * TILE_SIZE);
550  u->x_pos = x;
551  u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
552 
553  safe_y = Clamp(u->y_pos, 0, Map::MaxY() * TILE_SIZE);
554  u->z_pos = GetSlopePixelZ(safe_x, safe_y);
555  u->sprite_cache.sprite_seq.CopyWithoutPalette(v->sprite_cache.sprite_seq); // the shadow is never coloured
556 
558 
559  u = u->Next();
560  if (u != nullptr) {
561  u->x_pos = x;
562  u->y_pos = y;
563  u->z_pos = z + ROTOR_Z_OFFSET;
564 
566  }
567 }
568 
574 {
575  v->subspeed = 0;
576  v->progress = 0;
577 
578  Aircraft *u = v->Next();
579  u->vehstatus |= VS_HIDDEN;
580  u = u->Next();
581  if (u != nullptr) {
582  u->vehstatus |= VS_HIDDEN;
583  u->cur_speed = 0;
584  }
585 
586  SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
587 }
588 
589 static void PlayAircraftSound(const Vehicle *v)
590 {
591  if (!PlayVehicleSound(v, VSE_START)) {
592  SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
593  }
594 }
595 
596 
603 void UpdateAircraftCache(Aircraft *v, bool update_range)
604 {
605  uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
606  if (max_speed != 0) {
607  /* Convert from original units to km-ish/h */
608  max_speed = (max_speed * 128) / 10;
609 
610  v->vcache.cached_max_speed = max_speed;
611  } else {
612  /* Use the default max speed of the vehicle. */
613  v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
614  }
615 
616  /* Update cargo aging period. */
618  Aircraft *u = v->Next(); // Shadow for mail
620 
621  /* Update aircraft range. */
622  if (update_range) {
623  v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
624  /* Squared it now so we don't have to do it later all the time. */
625  v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
626  }
627 }
628 
629 
633 static constexpr uint16_t SPEED_LIMIT_TAXI = 50;
634 static constexpr uint16_t SPEED_LIMIT_APPROACH = 230;
635 static constexpr uint16_t SPEED_LIMIT_BROKEN = 320;
636 static constexpr uint16_t SPEED_LIMIT_HOLD = 425;
637 static constexpr uint16_t SPEED_LIMIT_NONE = UINT16_MAX;
638 
646 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
647 {
655  uint spd = v->acceleration * 77;
656  uint8_t t;
657 
658  /* Adjust speed limits by plane speed factor to prevent taxiing
659  * and take-off speeds being too low. */
660  speed_limit *= _settings_game.vehicle.plane_speed;
661 
662  /* adjust speed for broken vehicles */
663  if (v->vehstatus & VS_AIRCRAFT_BROKEN) {
664  if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false;
665  speed_limit = std::min<uint>(speed_limit, SPEED_LIMIT_BROKEN);
666  }
667 
668  if (v->vcache.cached_max_speed < speed_limit) {
669  if (v->cur_speed < speed_limit) hard_limit = false;
670  speed_limit = v->vcache.cached_max_speed;
671  }
672 
673  v->subspeed = (t = v->subspeed) + (uint8_t)spd;
674 
675  /* Aircraft's current speed is used twice so that very fast planes are
676  * forced to slow down rapidly in the short distance needed. The magic
677  * value 16384 was determined to give similar results to the old speed/48
678  * method at slower speeds. This also results in less reduction at slow
679  * speeds to that aircraft do not get to taxi speed straight after
680  * touchdown. */
681  if (!hard_limit && v->cur_speed > speed_limit) {
682  speed_limit = v->cur_speed - std::max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
683  }
684 
685  spd = std::min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
686 
687  /* updates statusbar only if speed have changed to save CPU time */
688  if (spd != v->cur_speed) {
689  v->cur_speed = spd;
691  }
692 
693  /* Adjust distance moved by plane speed setting */
695 
696  /* Convert direction-independent speed into direction-dependent speed. (old movement method) */
697  spd = v->GetOldAdvanceSpeed(spd);
698 
699  spd += v->progress;
700  v->progress = (uint8_t)spd;
701  return spd >> 8;
702 }
703 
712 {
713  int safe_x = Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE);
714  int safe_y = Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE);
715  return TileHeight(TileVirtXY(safe_x, safe_y)) * TILE_HEIGHT;
716 }
717 
728 void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
729 {
730  int base_altitude = GetTileHeightBelowAircraft(v);
731  if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->subtype == AIR_HELICOPTER) {
733  }
734 
735  /* Make sure eastbound and westbound planes do not "crash" into each
736  * other by providing them with vertical separation
737  */
738  switch (v->direction) {
739  case DIR_N:
740  case DIR_NE:
741  case DIR_E:
742  case DIR_SE:
743  base_altitude += 10;
744  break;
745 
746  default: break;
747  }
748 
749  /* Make faster planes fly higher so that they can overtake slower ones */
750  base_altitude += std::min(20 * (v->vcache.cached_max_speed / 200) - 90, 0);
751 
752  if (min_level != nullptr) *min_level = base_altitude + AIRCRAFT_MIN_FLYING_ALTITUDE;
753  if (max_level != nullptr) *max_level = base_altitude + AIRCRAFT_MAX_FLYING_ALTITUDE;
754 }
755 
764 {
765  int tile_height = GetTileHeightBelowAircraft(v);
766 
768 }
769 
770 template <class T>
771 int GetAircraftFlightLevel(T *v, bool takeoff)
772 {
773  /* Aircraft is in flight. We want to enforce it being somewhere
774  * between the minimum and the maximum allowed altitude. */
775  int aircraft_min_altitude;
776  int aircraft_max_altitude;
777  GetAircraftFlightLevelBounds(v, &aircraft_min_altitude, &aircraft_max_altitude);
778  int aircraft_middle_altitude = (aircraft_min_altitude + aircraft_max_altitude) / 2;
779 
780  /* If those assumptions would be violated, aircraft would behave fairly strange. */
781  assert(aircraft_min_altitude < aircraft_middle_altitude);
782  assert(aircraft_middle_altitude < aircraft_max_altitude);
783 
784  int z = v->z_pos;
785  if (z < aircraft_min_altitude ||
786  (HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z < aircraft_middle_altitude)) {
787  /* Ascend. And don't fly into that mountain right ahead.
788  * And avoid our aircraft become a stairclimber, so if we start
789  * correcting altitude, then we stop correction not too early. */
791  z += takeoff ? 2 : 1;
792  } else if (!takeoff && (z > aircraft_max_altitude ||
793  (HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z > aircraft_middle_altitude))) {
794  /* Descend lower. You are an aircraft, not an space ship.
795  * And again, don't stop correcting altitude too early. */
797  z--;
798  } else if (HasBit(v->flags, VAF_IN_MIN_HEIGHT_CORRECTION) && z >= aircraft_middle_altitude) {
799  /* Now, we have corrected altitude enough. */
801  } else if (HasBit(v->flags, VAF_IN_MAX_HEIGHT_CORRECTION) && z <= aircraft_middle_altitude) {
802  /* Now, we have corrected altitude enough. */
804  }
805 
806  return z;
807 }
808 
809 template int GetAircraftFlightLevel(DisasterVehicle *v, bool takeoff);
810 template int GetAircraftFlightLevel(Aircraft *v, bool takeoff);
811 
826 static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
827 {
828  assert(v != nullptr);
829  assert(apc != nullptr);
830 
831  /* In the case the station doesn't exit anymore, set target tile 0.
832  * It doesn't hurt much, aircraft will go to next order, nearest hangar
833  * or it will simply crash in next tick */
834  TileIndex tile = 0;
835 
836  const Station *st = Station::GetIfValid(v->targetairport);
837  if (st != nullptr) {
838  /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
839  tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
840  }
841 
842  int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
843  int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
844 
845  DiagDirection dir;
846  if (abs(delta_y) < abs(delta_x)) {
847  /* We are northeast or southwest of the airport */
848  dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
849  } else {
850  /* We are northwest or southeast of the airport */
851  dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
852  }
853  dir = ChangeDiagDir(dir, DiagDirDifference(DIAGDIR_NE, DirToDiagDir(rotation)));
854  return apc->entry_points[dir];
855 }
856 
857 
858 static void MaybeCrashAirplane(Aircraft *v);
859 
868 {
869  /* nullptr if station is invalid */
870  const Station *st = Station::GetIfValid(v->targetairport);
871  /* INVALID_TILE if there is no station */
872  TileIndex tile = INVALID_TILE;
873  Direction rotation = DIR_N;
874  uint size_x = 1, size_y = 1;
875  if (st != nullptr) {
876  if (st->airport.tile != INVALID_TILE) {
877  tile = st->airport.tile;
878  rotation = st->airport.rotation;
879  size_x = st->airport.w;
880  size_y = st->airport.h;
881  } else {
882  tile = st->xy;
883  }
884  }
885  /* DUMMY if there is no station or no airport */
886  const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
887 
888  /* prevent going to INVALID_TILE if airport is deleted. */
889  if (st == nullptr || st->airport.tile == INVALID_TILE) {
890  /* Jump into our "holding pattern" state machine if possible */
891  if (v->pos >= afc->nofelements) {
892  v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
893  } else if (v->targetairport != v->current_order.GetDestination()) {
894  /* If not possible, just get out of here fast */
895  v->state = FLYING;
898  /* get aircraft back on running altitude */
899  SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
900  return false;
901  }
902  }
903 
904  /* get airport moving data */
905  const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
906 
907  int x = TileX(tile) * TILE_SIZE;
908  int y = TileY(tile) * TILE_SIZE;
909 
910  /* Helicopter raise */
911  if (amd.flag & AMED_HELI_RAISE) {
912  Aircraft *u = v->Next()->Next();
913 
914  /* Make sure the rotors don't rotate too fast */
915  if (u->cur_speed > 32) {
916  v->cur_speed = 0;
917  if (--u->cur_speed == 32) {
918  if (!PlayVehicleSound(v, VSE_START)) {
919  SoundID sfx = AircraftVehInfo(v->engine_type)->sfx;
920  /* For compatibility with old NewGRF we ignore the sfx property, unless a NewGRF-defined sound is used.
921  * The baseset has only one helicopter sound, so this only limits using plane or cow sounds. */
923  SndPlayVehicleFx(sfx, v);
924  }
925  }
926  } else {
927  u->cur_speed = 32;
928  int count = UpdateAircraftSpeed(v);
929  if (count > 0) {
930  v->tile = 0;
931 
932  int z_dest;
933  GetAircraftFlightLevelBounds(v, &z_dest, nullptr);
934 
935  /* Reached altitude? */
936  if (v->z_pos >= z_dest) {
937  v->cur_speed = 0;
938  return true;
939  }
940  SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z_dest));
941  }
942  }
943  return false;
944  }
945 
946  /* Helicopter landing. */
947  if (amd.flag & AMED_HELI_LOWER) {
949 
950  if (st == nullptr) {
951  /* FIXME - AircraftController -> if station no longer exists, do not land
952  * helicopter will circle until sign disappears, then go to next order
953  * what to do when it is the only order left, right now it just stays in 1 place */
954  v->state = FLYING;
957  return false;
958  }
959 
960  /* Vehicle is now at the airport.
961  * Helicopter has arrived at the target landing pad, so the current position is also where it should land.
962  * Except for Oilrigs which are special due to being a 1x1 station, and helicopters land outside it. */
963  if (st->airport.type != AT_OILRIG) {
964  x = v->x_pos;
965  y = v->y_pos;
966  tile = TileVirtXY(x, y);
967  }
968  v->tile = tile;
969 
970  /* Find altitude of landing position. */
971  int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
972 
973  if (z == v->z_pos) {
974  Vehicle *u = v->Next()->Next();
975 
976  /* Increase speed of rotors. When speed is 80, we've landed. */
977  if (u->cur_speed >= 80) {
979  return true;
980  }
981  u->cur_speed += 4;
982  } else {
983  int count = UpdateAircraftSpeed(v);
984  if (count > 0) {
985  if (v->z_pos > z) {
986  SetAircraftPosition(v, v->x_pos, v->y_pos, std::max(v->z_pos - count, z));
987  } else {
988  SetAircraftPosition(v, v->x_pos, v->y_pos, std::min(v->z_pos + count, z));
989  }
990  }
991  }
992  return false;
993  }
994 
995  /* Get distance from destination pos to current pos. */
996  uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
997 
998  /* Need exact position? */
999  if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
1000 
1001  /* At final pos? */
1002  if (dist == 0) {
1003  /* Change direction smoothly to final direction. */
1004  DirDiff dirdiff = DirDifference(amd.direction, v->direction);
1005  /* if distance is 0, and plane points in right direction, no point in calling
1006  * UpdateAircraftSpeed(). So do it only afterwards */
1007  if (dirdiff == DIRDIFF_SAME) {
1008  v->cur_speed = 0;
1009  return true;
1010  }
1011 
1012  if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
1013 
1015  v->cur_speed >>= 1;
1016 
1017  SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1018  return false;
1019  }
1020 
1022  MaybeCrashAirplane(v);
1023  if ((v->vehstatus & VS_CRASHED) != 0) return false;
1024  }
1025 
1026  uint speed_limit = SPEED_LIMIT_TAXI;
1027  bool hard_limit = true;
1028 
1029  if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
1030  if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
1031  if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
1032  if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
1033 
1034  int count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
1035  if (count == 0) return false;
1036 
1037  /* If the plane will be a few subpixels away from the destination after
1038  * this movement loop, start nudging it towards the exact position for
1039  * the whole loop. Otherwise, heavily depending on the speed of the plane,
1040  * it is possible we totally overshoot the target, causing the plane to
1041  * make a loop, and trying again, and again, and again .. */
1042  bool nudge_towards_target = static_cast<uint>(count) + 3 > dist;
1043 
1044  if (v->turn_counter != 0) v->turn_counter--;
1045 
1046  do {
1047 
1049 
1050  if (nudge_towards_target || (amd.flag & AMED_LAND)) {
1051  /* move vehicle one pixel towards target */
1052  gp.x = (v->x_pos != (x + amd.x)) ?
1053  v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
1054  v->x_pos;
1055  gp.y = (v->y_pos != (y + amd.y)) ?
1056  v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
1057  v->y_pos;
1058 
1059  /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
1060  gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
1061 
1062  } else {
1063 
1064  /* Turn. Do it slowly if in the air. */
1065  Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
1066  if (newdir != v->direction) {
1067  if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
1068  if (v->turn_counter == 0 || newdir == v->last_direction) {
1069  if (newdir == v->last_direction) {
1070  v->number_consecutive_turns = 0;
1071  } else {
1073  }
1075  v->last_direction = v->direction;
1076  v->direction = newdir;
1077  }
1078 
1079  /* Move vehicle. */
1080  gp = GetNewVehiclePos(v);
1081  } else {
1082  v->cur_speed >>= 1;
1083  v->direction = newdir;
1084 
1085  /* When leaving a terminal an aircraft often goes to a position
1086  * directly in front of it. If it would move while turning it
1087  * would need an two extra turns to end up at the correct position.
1088  * To make it easier just disallow all moving while turning as
1089  * long as an aircraft is on the ground. */
1090  gp.x = v->x_pos;
1091  gp.y = v->y_pos;
1092  gp.new_tile = gp.old_tile = v->tile;
1093  }
1094  } else {
1095  v->number_consecutive_turns = 0;
1096  /* Move vehicle. */
1097  gp = GetNewVehiclePos(v);
1098  }
1099  }
1100 
1101  v->tile = gp.new_tile;
1102  /* If vehicle is in the air, use tile coordinate 0. */
1103  if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
1104 
1105  /* Adjust Z for land or takeoff? */
1106  int z = v->z_pos;
1107 
1108  if (amd.flag & AMED_TAKEOFF) {
1109  z = GetAircraftFlightLevel(v, true);
1110  } else if (amd.flag & AMED_HOLD) {
1111  /* Let the plane drop from normal flight altitude to holding pattern altitude */
1112  if (z > GetAircraftHoldMaxAltitude(v)) z--;
1113  } else if ((amd.flag & AMED_SLOWTURN) && (amd.flag & AMED_NOSPDCLAMP)) {
1114  z = GetAircraftFlightLevel(v);
1115  }
1116 
1117  /* NewGRF airports (like a rotated intercontinental from OpenGFX+Airports) can be non-rectangular
1118  * and their primary (north-most) tile does not have to be part of the airport.
1119  * As such, the height of the primary tile can be different from the rest of the airport.
1120  * Given we are landing/breaking, and as such are not a helicopter, we know that there has to be a hangar.
1121  * We also know that the airport itself has to be completely flat (otherwise it is not a valid airport).
1122  * Therefore, use the height of this hangar to calculate our z-value. */
1123  int airport_z = v->z_pos;
1124  if ((amd.flag & (AMED_LAND | AMED_BRAKE)) && st != nullptr) {
1125  assert(st->airport.HasHangar());
1126  TileIndex hangar_tile = st->airport.GetHangarTile(0);
1127  airport_z = GetTileMaxPixelZ(hangar_tile) + 1; // To avoid clashing with the shadow
1128  }
1129 
1130  if (amd.flag & AMED_LAND) {
1131  if (st->airport.tile == INVALID_TILE) {
1132  /* Airport has been removed, abort the landing procedure */
1133  v->state = FLYING;
1136  /* get aircraft back on running altitude */
1137  SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
1138  continue;
1139  }
1140 
1141  /* We're not flying below our destination, right? */
1142  assert(airport_z <= z);
1143  int t = std::max(1U, dist - 4);
1144  int delta = z - airport_z;
1145 
1146  /* Only start lowering when we're sufficiently close for a 1:1 glide */
1147  if (delta >= t) {
1148  z -= CeilDiv(z - airport_z, t);
1149  }
1150  if (z < airport_z) z = airport_z;
1151  }
1152 
1153  /* We've landed. Decrease speed when we're reaching end of runway. */
1154  if (amd.flag & AMED_BRAKE) {
1155 
1156  if (z > airport_z) {
1157  z--;
1158  } else if (z < airport_z) {
1159  z++;
1160  }
1161 
1162  }
1163 
1164  SetAircraftPosition(v, gp.x, gp.y, z);
1165  } while (--count != 0);
1166  return false;
1167 }
1168 
1174 {
1175  v->crashed_counter += 3;
1176 
1178 
1179  /* make aircraft crash down to the ground */
1180  if (v->crashed_counter < 500 && st == nullptr && ((v->crashed_counter % 3) == 0) ) {
1181  int z = GetSlopePixelZ(Clamp(v->x_pos, 0, Map::MaxX() * TILE_SIZE), Clamp(v->y_pos, 0, Map::MaxY() * TILE_SIZE));
1182  v->z_pos -= 1;
1183  if (v->z_pos <= z) {
1184  v->crashed_counter = 500;
1185  v->z_pos = z + 1;
1186  } else {
1187  v->crashed_counter = 0;
1188  }
1189  SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1190  }
1191 
1192  if (v->crashed_counter < 650) {
1193  uint32_t r;
1194  if (Chance16R(1, 32, r)) {
1195  static const DirDiff delta[] = {
1197  };
1198 
1199  v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
1200  SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1201  r = Random();
1203  GB(r, 0, 4) - 4,
1204  GB(r, 4, 4) - 4,
1205  GB(r, 8, 4),
1207  }
1208  } else if (v->crashed_counter >= 10000) {
1209  /* remove rubble of crashed airplane */
1210 
1211  /* clear runway-in on all airports, set by crashing plane
1212  * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
1213  * but they all share the same number */
1214  if (st != nullptr) {
1215  CLRBITS(st->airport.flags, RUNWAY_IN_block);
1216  CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
1217  CLRBITS(st->airport.flags, RUNWAY_IN2_block); // intercontinental
1218  }
1219 
1220  delete v;
1221 
1222  return false;
1223  }
1224 
1225  return true;
1226 }
1227 
1228 
1234 static void HandleAircraftSmoke(Aircraft *v, bool mode)
1235 {
1236  static const struct {
1237  int8_t x;
1238  int8_t y;
1239  } smoke_pos[] = {
1240  { 5, 5 },
1241  { 6, 0 },
1242  { 5, -5 },
1243  { 0, -6 },
1244  { -5, -5 },
1245  { -6, 0 },
1246  { -5, 5 },
1247  { 0, 6 }
1248  };
1249 
1250  if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
1251 
1252  /* Stop smoking when landed */
1253  if (v->cur_speed < 10) {
1255  v->breakdown_ctr = 0;
1256  return;
1257  }
1258 
1259  /* Spawn effect et most once per Tick, i.e. !mode */
1260  if (!mode && (v->tick_counter & 0x0F) == 0) {
1262  smoke_pos[v->direction].x,
1263  smoke_pos[v->direction].y,
1264  2,
1266  );
1267  }
1268 }
1269 
1270 void HandleMissingAircraftOrders(Aircraft *v)
1271 {
1272  /*
1273  * We do not have an order. This can be divided into two cases:
1274  * 1) we are heading to an invalid station. In this case we must
1275  * find another airport to go to. If there is nowhere to go,
1276  * we will destroy the aircraft as it otherwise will enter
1277  * the holding pattern for the first airport, which can cause
1278  * the plane to go into an undefined state when building an
1279  * airport with the same StationID.
1280  * 2) we are (still) heading to a (still) valid airport, then we
1281  * can continue going there. This can happen when you are
1282  * changing the aircraft's orders while in-flight or in for
1283  * example a depot. However, when we have a current order to
1284  * go to a depot, we have to keep that order so the aircraft
1285  * actually stops.
1286  */
1287  const Station *st = GetTargetAirportIfValid(v);
1288  if (st == nullptr) {
1289  Backup<CompanyID> cur_company(_current_company, v->owner);
1291  cur_company.Restore();
1292 
1293  if (ret.Failed()) CrashAirplane(v);
1294  } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
1295  v->current_order.Free();
1296  }
1297 }
1298 
1299 
1300 TileIndex Aircraft::GetOrderStationLocation(StationID)
1301 {
1302  /* Orders are changed in flight, ensure going to the right station. */
1303  if (this->state == FLYING) {
1305  }
1306 
1307  /* Aircraft do not use dest-tile */
1308  return 0;
1309 }
1310 
1312 {
1313  this->colourmap = PAL_NONE;
1314  this->UpdateViewport(true, false);
1315  if (this->subtype == AIR_HELICOPTER) {
1316  GetRotorImage(this, EIT_ON_MAP, &this->Next()->Next()->sprite_cache.sprite_seq);
1317  }
1318 }
1319 
1320 
1321 uint Aircraft::Crash(bool flooded)
1322 {
1323  uint victims = Vehicle::Crash(flooded) + 2; // pilots
1324  this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
1325 
1326  return victims;
1327 }
1328 
1333 static void CrashAirplane(Aircraft *v)
1334 {
1336 
1337  uint victims = v->Crash();
1338  SetDParam(0, victims);
1339 
1340  v->cargo.Truncate();
1341  v->Next()->cargo.Truncate();
1342  const Station *st = GetTargetAirportIfValid(v);
1343  StringID newsitem;
1344  TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1345  if (st == nullptr) {
1346  newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
1347  } else {
1348  SetDParam(1, st->index);
1349  newsitem = STR_NEWS_AIRCRAFT_CRASH;
1350  }
1351 
1352  AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims));
1353  Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims));
1354 
1355  NewsType newstype = NT_ACCIDENT;
1356  if (v->owner != _local_company) {
1357  newstype = NT_ACCIDENT_OTHER;
1358  }
1359 
1360  AddTileNewsItem(newsitem, newstype, vt, nullptr, st != nullptr ? st->index : INVALID_STATION);
1361 
1362  ModifyStationRatingAround(vt, v->owner, -160, 30);
1363  if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1364 }
1365 
1371 {
1372 
1374 
1375  uint32_t prob;
1377  (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
1379  prob = 3276;
1380  } else {
1381  if (_settings_game.vehicle.plane_crashes == 0) return;
1382  prob = (0x4000 << _settings_game.vehicle.plane_crashes) / 1500;
1383  }
1384 
1385  if (GB(Random(), 0, 22) > prob) return;
1386 
1387  /* Crash the airplane. Remove all goods stored at the station. */
1388  for (GoodsEntry &ge : st->goods) {
1389  ge.rating = 1;
1390  ge.cargo.Truncate();
1391  }
1392 
1393  CrashAirplane(v);
1394 }
1395 
1402 {
1403  if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
1404 
1407 
1408  /* Check if station was ever visited before */
1409  if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
1410  st->had_vehicle_of_type |= HVOT_AIRCRAFT;
1411  SetDParam(0, st->index);
1412  /* show newsitem of celebrating citizens */
1414  STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
1416  v->index,
1417  st->index
1418  );
1419  AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
1420  Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
1421  }
1422 
1423  v->BeginLoading();
1424 }
1425 
1431 {
1433 
1434  TileIndex vt = TileVirtXY(v->x_pos, v->y_pos);
1435 
1436  v->UpdateDeltaXY();
1437 
1438  AirportTileAnimationTrigger(st, vt, AAT_STATION_AIRPLANE_LAND);
1439 
1440  if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
1441  SndPlayVehicleFx(SND_17_SKID_PLANE, v);
1442  }
1443 }
1444 
1445 
1448 {
1449  if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
1451  }
1452 
1453  const Station *st = GetTargetAirportIfValid(v);
1454  const AirportFTAClass *apc = st == nullptr ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
1455  Direction rotation = st == nullptr ? DIR_N : st->airport.rotation;
1456  v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
1457 }
1458 
1468 {
1469  v->cur_speed = 0;
1470  v->subspeed = 0;
1471  v->progress = 0;
1472  v->direction = exit_dir;
1473  v->vehstatus &= ~VS_HIDDEN;
1474  {
1475  Vehicle *u = v->Next();
1476  u->vehstatus &= ~VS_HIDDEN;
1477 
1478  /* Rotor blades */
1479  u = u->Next();
1480  if (u != nullptr) {
1481  u->vehstatus &= ~VS_HIDDEN;
1482  u->cur_speed = 80;
1483  }
1484  }
1485 
1487  v->LeaveUnbunchingDepot();
1488  SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
1491 }
1492 
1496 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
1497 {
1499  v->state = apc->layout[v->pos].heading;
1500 }
1501 
1508 {
1509  VehicleEnterDepot(v);
1510  v->state = apc->layout[v->pos].heading;
1511 }
1512 
1519 {
1520  /* if we just arrived, execute EnterHangar first */
1521  if (v->previous_pos != v->pos) {
1523  return;
1524  }
1525 
1526  /* if we were sent to the depot, stay there */
1527  if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
1528  v->current_order.Free();
1529  return;
1530  }
1531 
1532  /* Check if we should wait here for unbunching. */
1533  if (v->IsWaitingForUnbunching()) return;
1534 
1535  if (!v->current_order.IsType(OT_GOTO_STATION) &&
1536  !v->current_order.IsType(OT_GOTO_DEPOT))
1537  return;
1538 
1539  /* We are leaving a hangar, but have to go to the exact same one; re-enter */
1540  if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
1541  VehicleEnterDepot(v);
1542  return;
1543  }
1544 
1545  /* if the block of the next position is busy, stay put */
1546  if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1547 
1548  /* We are already at the target airport, we need to find a terminal */
1549  if (v->current_order.GetDestination() == v->targetairport) {
1550  /* FindFreeTerminal:
1551  * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
1552  if (v->subtype == AIR_HELICOPTER) {
1553  if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
1554  } else {
1555  if (!AirportFindFreeTerminal(v, apc)) return; // airplane
1556  }
1557  } else { // Else prepare for launch.
1558  /* airplane goto state takeoff, helicopter to helitakeoff */
1559  v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
1560  }
1561  const Station *st = Station::GetByTile(v->tile);
1563  AirportMove(v, apc);
1564 }
1565 
1568 {
1569  /* if we just arrived, execute EnterTerminal first */
1570  if (v->previous_pos != v->pos) {
1571  AircraftEventHandler_EnterTerminal(v, apc);
1572  /* on an airport with helipads, a helicopter will always land there
1573  * and get serviced at the same time - setting */
1575  if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
1576  /* an excerpt of ServiceAircraft, without the invisibility stuff */
1580  v->reliability = v->GetEngine()->reliability;
1582  }
1583  }
1584  return;
1585  }
1586 
1587  if (v->current_order.IsType(OT_NOTHING)) return;
1588 
1589  /* if the block of the next position is busy, stay put */
1590  if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1591 
1592  /* airport-road is free. We either have to go to another airport, or to the hangar
1593  * ---> start moving */
1594 
1595  bool go_to_hangar = false;
1596  switch (v->current_order.GetType()) {
1597  case OT_GOTO_STATION: // ready to fly to another airport
1598  break;
1599  case OT_GOTO_DEPOT: // visit hangar for servicing, sale, etc.
1600  go_to_hangar = v->current_order.GetDestination() == v->targetairport;
1601  break;
1602  case OT_CONDITIONAL:
1603  /* In case of a conditional order we just have to wait a tick
1604  * longer, so the conditional order can actually be processed;
1605  * we should not clear the order as that makes us go nowhere. */
1606  return;
1607  default: // orders have been deleted (no orders), goto depot and don't bother us
1608  v->current_order.Free();
1609  go_to_hangar = true;
1610  }
1611 
1612  if (go_to_hangar && Station::Get(v->targetairport)->airport.HasHangar()) {
1613  v->state = HANGAR;
1614  } else {
1615  /* airplane goto state takeoff, helicopter to helitakeoff */
1616  v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
1617  }
1618  AirportMove(v, apc);
1619 }
1620 
1621 static void AircraftEventHandler_General(Aircraft *, const AirportFTAClass *)
1622 {
1623  FatalError("OK, you shouldn't be here, check your Airport Scheme!");
1624 }
1625 
1626 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *)
1627 {
1628  PlayAircraftSound(v); // play takeoffsound for airplanes
1629  v->state = STARTTAKEOFF;
1630 }
1631 
1632 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *)
1633 {
1634  v->state = ENDTAKEOFF;
1635  v->UpdateDeltaXY();
1636 }
1637 
1638 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *)
1639 {
1640  v->state = FLYING;
1641  /* get the next position to go to, differs per airport */
1643 }
1644 
1645 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *)
1646 {
1647  v->state = FLYING;
1648  v->UpdateDeltaXY();
1649 
1650  /* get the next position to go to, differs per airport */
1652 
1653  /* Send the helicopter to a hangar if needed for replacement */
1654  if (v->NeedsAutomaticServicing()) {
1655  Backup<CompanyID> cur_company(_current_company, v->owner);
1657  cur_company.Restore();
1658  }
1659 }
1660 
1661 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
1662 {
1664 
1665  /* Runway busy, not allowed to use this airstation or closed, circle. */
1666  if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
1667  /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
1668  * if it is an airplane, look for LANDING, for helicopter HELILANDING
1669  * it is possible to choose from multiple landing runways, so loop until a free one is found */
1670  uint8_t landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
1671  const AirportFTA *current = apc->layout[v->pos].next;
1672  while (current != nullptr) {
1673  if (current->heading == landingtype) {
1674  /* save speed before, since if AirportHasBlock is false, it resets them to 0
1675  * we don't want that for plane in air
1676  * hack for speed thingie */
1677  uint16_t tcur_speed = v->cur_speed;
1678  uint16_t tsubspeed = v->subspeed;
1679  if (!AirportHasBlock(v, current, apc)) {
1680  v->state = landingtype; // LANDING / HELILANDING
1682  /* it's a bit dirty, but I need to set position to next position, otherwise
1683  * if there are multiple runways, plane won't know which one it took (because
1684  * they all have heading LANDING). And also occupy that block! */
1685  v->pos = current->next_position;
1686  SETBITS(st->airport.flags, apc->layout[v->pos].block);
1687  return;
1688  }
1689  v->cur_speed = tcur_speed;
1690  v->subspeed = tsubspeed;
1691  }
1692  current = current->next;
1693  }
1694  }
1695  v->state = FLYING;
1696  v->pos = apc->layout[v->pos].next_position;
1697 }
1698 
1699 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *)
1700 {
1701  v->state = ENDLANDING;
1702  AircraftLandAirplane(v); // maybe crash airplane
1703 
1704  /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
1705  if (v->NeedsAutomaticServicing()) {
1706  Backup<CompanyID> cur_company(_current_company, v->owner);
1708  cur_company.Restore();
1709  }
1710 }
1711 
1712 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *)
1713 {
1714  v->state = HELIENDLANDING;
1715  v->UpdateDeltaXY();
1716 }
1717 
1718 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
1719 {
1720  /* next block busy, don't do a thing, just wait */
1721  if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1722 
1723  /* if going to terminal (OT_GOTO_STATION) choose one
1724  * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
1725  * 2. not going for terminal (but depot, no order),
1726  * --> get out of the way to the hangar. */
1727  if (v->current_order.IsType(OT_GOTO_STATION)) {
1728  if (AirportFindFreeTerminal(v, apc)) return;
1729  }
1730  v->state = HANGAR;
1731 
1732 }
1733 
1734 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
1735 {
1736  /* next block busy, don't do a thing, just wait */
1737  if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
1738 
1739  /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
1740  * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
1741  * 2. not going for terminal (but depot, no order),
1742  * --> get out of the way to the hangar IF there are terminals on the airport.
1743  * --> else TAKEOFF
1744  * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
1745  * must go to a hangar. */
1746  if (v->current_order.IsType(OT_GOTO_STATION)) {
1747  if (AirportFindFreeHelipad(v, apc)) return;
1748  }
1750 }
1751 
1757 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
1760  AircraftEventHandler_General, // TO_ALL = 0
1761  AircraftEventHandler_InHangar, // HANGAR = 1
1762  AircraftEventHandler_AtTerminal, // TERM1 = 2
1763  AircraftEventHandler_AtTerminal, // TERM2 = 3
1764  AircraftEventHandler_AtTerminal, // TERM3 = 4
1765  AircraftEventHandler_AtTerminal, // TERM4 = 5
1766  AircraftEventHandler_AtTerminal, // TERM5 = 6
1767  AircraftEventHandler_AtTerminal, // TERM6 = 7
1768  AircraftEventHandler_AtTerminal, // HELIPAD1 = 8
1769  AircraftEventHandler_AtTerminal, // HELIPAD2 = 9
1770  AircraftEventHandler_TakeOff, // TAKEOFF = 10
1771  AircraftEventHandler_StartTakeOff, // STARTTAKEOFF = 11
1772  AircraftEventHandler_EndTakeOff, // ENDTAKEOFF = 12
1773  AircraftEventHandler_HeliTakeOff, // HELITAKEOFF = 13
1774  AircraftEventHandler_Flying, // FLYING = 14
1775  AircraftEventHandler_Landing, // LANDING = 15
1776  AircraftEventHandler_EndLanding, // ENDLANDING = 16
1777  AircraftEventHandler_HeliLanding, // HELILANDING = 17
1778  AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
1779  AircraftEventHandler_AtTerminal, // TERM7 = 19
1780  AircraftEventHandler_AtTerminal, // TERM8 = 20
1781  AircraftEventHandler_AtTerminal, // HELIPAD3 = 21
1782 };
1783 
1784 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
1785 {
1786  /* we have left the previous block, and entered the new one. Free the previous block */
1787  if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
1789 
1790  CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
1791  }
1792 }
1793 
1794 static void AirportGoToNextPosition(Aircraft *v)
1795 {
1796  /* if aircraft is not in position, wait until it is */
1797  if (!AircraftController(v)) return;
1798 
1800 
1801  AirportClearBlock(v, apc);
1802  AirportMove(v, apc); // move aircraft to next position
1803 }
1804 
1805 /* gets pos from vehicle and next orders */
1806 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
1807 {
1808  /* error handling */
1809  if (v->pos >= apc->nofelements) {
1810  Debug(misc, 0, "[Ap] position {} is not valid for current airport. Max position is {}", v->pos, apc->nofelements-1);
1811  assert(v->pos < apc->nofelements);
1812  }
1813 
1814  const AirportFTA *current = &apc->layout[v->pos];
1815  /* we have arrived in an important state (eg terminal, hangar, etc.) */
1816  if (current->heading == v->state) {
1817  uint8_t prev_pos = v->pos; // location could be changed in state, so save it before-hand
1818  uint8_t prev_state = v->state;
1819  _aircraft_state_handlers[v->state](v, apc);
1820  if (v->state != FLYING) v->previous_pos = prev_pos;
1821  if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
1822  return true;
1823  }
1824 
1825  v->previous_pos = v->pos; // save previous location
1826 
1827  /* there is only one choice to move to */
1828  if (current->next == nullptr) {
1829  if (AirportSetBlocks(v, current, apc)) {
1830  v->pos = current->next_position;
1832  } // move to next position
1833  return false;
1834  }
1835 
1836  /* there are more choices to choose from, choose the one that
1837  * matches our heading */
1838  do {
1839  if (v->state == current->heading || current->heading == TO_ALL) {
1840  if (AirportSetBlocks(v, current, apc)) {
1841  v->pos = current->next_position;
1843  } // move to next position
1844  return false;
1845  }
1846  current = current->next;
1847  } while (current != nullptr);
1848 
1849  Debug(misc, 0, "[Ap] cannot move further on Airport! (pos {} state {}) for vehicle {}", v->pos, v->state, v->index);
1850  NOT_REACHED();
1851 }
1852 
1854 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1855 {
1856  const AirportFTA *reference = &apc->layout[v->pos];
1857  const AirportFTA *next = &apc->layout[current_pos->next_position];
1858 
1859  /* same block, then of course we can move */
1860  if (apc->layout[current_pos->position].block != next->block) {
1861  const Station *st = Station::Get(v->targetairport);
1862  uint64_t airport_flags = next->block;
1863 
1864  /* check additional possible extra blocks */
1865  if (current_pos != reference && current_pos->block != NOTHING_block) {
1866  airport_flags |= current_pos->block;
1867  }
1868 
1869  if (st->airport.flags & airport_flags) {
1870  v->cur_speed = 0;
1871  v->subspeed = 0;
1872  return true;
1873  }
1874  }
1875  return false;
1876 }
1877 
1885 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
1886 {
1887  const AirportFTA *next = &apc->layout[current_pos->next_position];
1888  const AirportFTA *reference = &apc->layout[v->pos];
1889 
1890  /* if the next position is in another block, check it and wait until it is free */
1891  if ((apc->layout[current_pos->position].block & next->block) != next->block) {
1892  uint64_t airport_flags = next->block;
1893  /* search for all all elements in the list with the same state, and blocks != N
1894  * this means more blocks should be checked/set */
1895  const AirportFTA *current = current_pos;
1896  if (current == reference) current = current->next;
1897  while (current != nullptr) {
1898  if (current->heading == current_pos->heading && current->block != 0) {
1899  airport_flags |= current->block;
1900  break;
1901  }
1902  current = current->next;
1903  }
1904 
1905  /* if the block to be checked is in the next position, then exclude that from
1906  * checking, because it has been set by the airplane before */
1907  if (current_pos->block == next->block) airport_flags ^= next->block;
1908 
1910  if (st->airport.flags & airport_flags) {
1911  v->cur_speed = 0;
1912  v->subspeed = 0;
1913  return false;
1914  }
1915 
1916  if (next->block != NOTHING_block) {
1917  SETBITS(st->airport.flags, airport_flags); // occupy next block
1918  }
1919  }
1920  return true;
1921 }
1922 
1929  uint64_t airport_flag;
1930 };
1931 
1934  {TERM1, TERM1_block},
1935  {TERM2, TERM2_block},
1936  {TERM3, TERM3_block},
1937  {TERM4, TERM4_block},
1938  {TERM5, TERM5_block},
1939  {TERM6, TERM6_block},
1940  {TERM7, TERM7_block},
1941  {TERM8, TERM8_block},
1945 };
1946 
1954 static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal)
1955 {
1956  assert(last_terminal <= lengthof(_airport_terminal_mapping));
1958  for (; i < last_terminal; i++) {
1959  if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
1960  /* TERMINAL# HELIPAD# */
1961  v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
1962  SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
1963  return true;
1964  }
1965  }
1966  return false;
1967 }
1968 
1974 static uint GetNumTerminals(const AirportFTAClass *apc)
1975 {
1976  uint num = 0;
1977 
1978  for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
1979 
1980  return num;
1981 }
1982 
1990 {
1991  /* example of more terminalgroups
1992  * {0,HANGAR,NOTHING_block,1}, {0,TERMGROUP,TERM_GROUP1_block,0}, {0,TERMGROUP,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
1993  * Heading TERMGROUP denotes a group. We see 2 groups here:
1994  * 1. group 0 -- TERM_GROUP1_block (check block)
1995  * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
1996  * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
1997  * looks at the corresponding terminals of that group. If no free ones are found, other
1998  * possible groups are checked (in this case group 1, since that is after group 0). If that
1999  * fails, then attempt fails and plane waits
2000  */
2001  if (apc->terminals[0] > 1) {
2002  const Station *st = Station::Get(v->targetairport);
2003  const AirportFTA *temp = apc->layout[v->pos].next;
2004 
2005  while (temp != nullptr) {
2006  if (temp->heading == TERMGROUP) {
2007  if (!(st->airport.flags & temp->block)) {
2008  /* read which group do we want to go to?
2009  * (the first free group) */
2010  uint target_group = temp->next_position + 1;
2011 
2012  /* at what terminal does the group start?
2013  * that means, sum up all terminals of
2014  * groups with lower number */
2015  uint group_start = 0;
2016  for (uint i = 1; i < target_group; i++) {
2017  group_start += apc->terminals[i];
2018  }
2019 
2020  uint group_end = group_start + apc->terminals[target_group];
2021  if (FreeTerminal(v, group_start, group_end)) return true;
2022  }
2023  } else {
2024  /* once the heading isn't 255, we've exhausted the possible blocks.
2025  * So we cannot move */
2026  return false;
2027  }
2028  temp = temp->next;
2029  }
2030  }
2031 
2032  /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
2033  return FreeTerminal(v, 0, GetNumTerminals(apc));
2034 }
2035 
2043 {
2044  /* if an airport doesn't have helipads, use terminals */
2045  if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
2046 
2047  /* only 1 helicoptergroup, check all helipads
2048  * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
2050 }
2051 
2057 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
2058 {
2059  if (too_far) {
2060  if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
2063  AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
2064  if (v->owner == _local_company) {
2065  /* Post a news message. */
2066  SetDParam(0, v->index);
2067  AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
2068  }
2069  }
2070  return;
2071  }
2072 
2073  if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
2074  /* Not too far anymore, clear flag and message. */
2077  DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
2078  }
2079 }
2080 
2081 static bool AircraftEventHandler(Aircraft *v, int loop)
2082 {
2083  if (v->vehstatus & VS_CRASHED) {
2084  return HandleCrashedAircraft(v);
2085  }
2086 
2087  if (v->vehstatus & VS_STOPPED) return true;
2088 
2089  v->HandleBreakdown();
2090 
2091  HandleAircraftSmoke(v, loop != 0);
2092  ProcessOrders(v);
2093  v->HandleLoading(loop != 0);
2094 
2095  if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
2096 
2097  if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
2098  /* If we are flying, unconditionally clear the 'dest too far' state. */
2099  AircraftHandleDestTooFar(v, false);
2100  } else if (v->acache.cached_max_range_sqr != 0) {
2101  /* Check the distance to the next destination. This code works because the target
2102  * airport is only updated after take off and not on the ground. */
2104  Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : nullptr;
2105 
2106  if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) {
2107  uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
2108  AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
2109  }
2110  }
2111 
2112  if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
2113 
2114  return true;
2115 }
2116 
2118 {
2119  if (!this->IsNormalAircraft()) return true;
2120 
2122 
2123  this->tick_counter++;
2124 
2125  if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
2126 
2127  if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
2128 
2129  this->current_order_time++;
2130 
2131  for (uint i = 0; i != 2; i++) {
2132  /* stop if the aircraft was deleted */
2133  if (!AircraftEventHandler(this, i)) return false;
2134  }
2135 
2136  return true;
2137 }
2138 
2139 
2147 {
2148  assert(v->type == VEH_AIRCRAFT);
2149 
2151  if (st == nullptr) return nullptr;
2152 
2153  return st->airport.tile == INVALID_TILE ? nullptr : st;
2154 }
2155 
2161 {
2162  /* only 1 station is updated per function call, so it is enough to get entry_point once */
2163  const AirportFTAClass *ap = st->airport.GetFTA();
2164  Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
2165 
2166  for (Aircraft *v : Aircraft::Iterate()) {
2167  if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
2168  assert(v->state == FLYING);
2169 
2170  Order *o = &v->current_order;
2171  /* The aircraft is heading to a hangar, but the new station doesn't have one,
2172  * or the aircraft can't land on the new station. Cancel current order. */
2173  if (o->IsType(OT_GOTO_DEPOT) && !(o->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) && o->GetDestination() == st->index &&
2174  (!st->airport.HasHangar() || !CanVehicleUseStation(v, st))) {
2175  o->MakeDummy();
2177  }
2178  v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
2180  }
2181 
2182  /* Heliports don't have a hangar. Invalidate all go to hangar orders from all aircraft. */
2183  if (!st->airport.HasHangar()) RemoveOrderFromAllVehicles(OT_GOTO_DEPOT, st->index, true);
2184 }
Base functions for all AIs.
Base for aircraft.
@ VAF_HELI_DIRECT_DESCENT
The helicopter is descending directly at its destination (helipad or in front of hangar)
Definition: aircraft.h:45
@ VAF_DEST_TOO_FAR
Next destination is too far away.
Definition: aircraft.h:37
@ VAF_IN_MIN_HEIGHT_CORRECTION
The vehicle is currently raising its altitude because it hit the lower bound.
Definition: aircraft.h:43
@ VAF_IN_MAX_HEIGHT_CORRECTION
The vehicle is currently lowering its altitude because it hit the upper bound.
Definition: aircraft.h:42
static constexpr int AIRCRAFT_MIN_FLYING_ALTITUDE
Base values for flight levels above ground level for 'normal' flight and holding patterns.
Definition: aircraft.h:20
static constexpr int HELICOPTER_HOLD_MAX_FLYING_ALTITUDE
holding flying altitude above tile of helicopters.
Definition: aircraft.h:23
static const int ROTOR_Z_OFFSET
Z Offset between helicopter- and rotorsprite.
Definition: aircraft.h:48
static constexpr int PLANE_HOLD_MAX_FLYING_ALTITUDE
holding flying altitude above tile of planes.
Definition: aircraft.h:22
@ AIR_AIRCRAFT
an airplane
Definition: aircraft.h:30
@ AIR_ROTOR
rotor of an helicopter
Definition: aircraft.h:32
@ AIR_SHADOW
shadow of the aircraft
Definition: aircraft.h:31
@ AIR_HELICOPTER
an helicopter
Definition: aircraft.h:29
static constexpr int AIRCRAFT_MAX_FLYING_ALTITUDE
Maximum flying altitude above tile.
Definition: aircraft.h:21
static void HandleAircraftSmoke(Aircraft *v, bool mode)
Handle smoke of broken aircraft.
static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
Handle aircraft movement/decision making in an airport hangar.
static StationID FindNearestHangar(const Aircraft *v)
Find the nearest hangar to v INVALID_STATION is returned, if the company does not have any suitable a...
static uint GetNumTerminals(const AirportFTAClass *apc)
Get the number of terminals at the airport.
Station * GetTargetAirportIfValid(const Aircraft *v)
Returns aircraft's target station if v->target_airport is a valid station with airport.
void UpdateAirplanesOnNewStation(const Station *st)
Updates the status of the Aircraft heading or in the station.
void AircraftNextAirportPos_and_Order(Aircraft *v)
set the right pos when heading to other airports after takeoff
int GetTileHeightBelowAircraft(const Vehicle *v)
Get the tile height below the aircraft.
void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
Get the size of the sprite of an aircraft sprite heading west (used for lists).
static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
Find the entry point to an airport depending on direction which the airport is being approached from.
static const MovementTerminalMapping _airport_terminal_mapping[]
A list of all valid terminals and their associated blocks.
CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
Build an aircraft.
static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
returns true if the road ahead is busy, eg.
static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
"reserve" a block for the plane
static void AircraftLandAirplane(Aircraft *v)
Aircraft touched down at the landing strip.
int GetAircraftHoldMaxAltitude(const Aircraft *v)
Gets the maximum 'flight level' for the holding pattern of the aircraft, in pixels 'z_pos' 0,...
static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal)
Find a free terminal or helipad, and if available, assign it.
static constexpr uint16_t SPEED_LIMIT_BROKEN
Maximum speed of an aircraft that is broken.
static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
Handle the 'dest too far' flag and the corresponding news message for aircraft.
static bool AircraftController(Aircraft *v)
Controls the movement of an aircraft.
void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
Aircraft is about to leave the hangar.
static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
Find a free helipad, and assign it if available.
static constexpr uint16_t SPEED_LIMIT_NONE
No environmental speed limit. Speed limit is type dependent.
static void AircraftEntersTerminal(Aircraft *v)
Aircraft arrives at a terminal.
static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
At one of the Airport's Terminals.
void SetAircraftPosition(Aircraft *v, int x, int y, int z)
Set aircraft position.
static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit=SPEED_LIMIT_NONE, bool hard_limit=true)
Sets the new speed for an aircraft.
static void MaybeCrashAirplane(Aircraft *v)
Decide whether aircraft v should crash.
static constexpr uint16_t SPEED_LIMIT_HOLD
Maximum speed of an aircraft that flies the holding pattern.
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min_level, int *max_level)
Get the 'flight level' bounds, in pixels from 'z_pos' 0 for a particular vehicle for normal flight si...
static bool HandleCrashedAircraft(Aircraft *v)
Handle crashed aircraft v.
void UpdateAircraftCache(Aircraft *v, bool update_range)
Update cached values of an aircraft.
HelicopterRotorStates
Helicopter rotor animation states.
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.
static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
Find a free terminal, and assign it if available.
void HandleAircraftEnterHangar(Aircraft *v)
Handle Aircraft specific tasks when an Aircraft enters a hangar.
static constexpr uint16_t SPEED_LIMIT_APPROACH
Maximum speed of an aircraft on finals.
static void CrashAirplane(Aircraft *v)
Bring the aircraft in a crashed state, create the explosion animation, and create a news item about t...
static constexpr uint16_t SPEED_LIMIT_TAXI
Special velocities for aircraft.
void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc)
Signature of the aircraft handler function.
Command definitions related to aircraft.
const AirportFTAClass * GetAirport(const uint8_t airport_type)
Get the finite state machine of an airport type.
Definition: airport.cpp:207
uint8_t GetVehiclePosOnBuild(TileIndex hangar_tile)
Get the vehicle position when an aircraft is build at the given tile.
Definition: airport.cpp:218
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:80
static const uint64_t TERM7_block
Block belonging to terminal 7.
Definition: airport.h:115
static const uint64_t TERM4_block
Block belonging to terminal 4.
Definition: airport.h:92
static const uint64_t TERM2_block
Block belonging to terminal 2.
Definition: airport.h:90
static const uint64_t TERM1_block
Movement Blocks on Airports blocks (eg_airport_flags).
Definition: airport.h:89
static const uint64_t TERM5_block
Block belonging to terminal 5.
Definition: airport.h:93
static const uint64_t TERM3_block
Block belonging to terminal 3.
Definition: airport.h:91
static const uint64_t TERM8_block
Block belonging to terminal 8.
Definition: airport.h:116
static const uint MAX_TERMINALS
Some airport-related constants.
Definition: airport.h:17
@ AT_DUMMY
Dummy airport.
Definition: airport.h:43
@ AT_OILRIG
Oilrig airport.
Definition: airport.h:38
@ AMED_BRAKE
Taxiing at the airport.
Definition: airport.h:53
@ AMED_HOLD
Holding pattern movement (above the airport).
Definition: airport.h:56
@ AMED_SLOWTURN
Turn slowly (mostly used in the air).
Definition: airport.h:50
@ AMED_HELI_LOWER
Helicopter landing.
Definition: airport.h:55
@ AMED_LAND
Landing onto landing strip.
Definition: airport.h:51
@ AMED_NOSPDCLAMP
No speed restrictions.
Definition: airport.h:48
@ AMED_TAKEOFF
Takeoff movement.
Definition: airport.h:49
@ AMED_EXACTPOS
Go exactly to the destination coordinates.
Definition: airport.h:52
@ AMED_HELI_RAISE
Helicopter take-off.
Definition: airport.h:54
static const uint64_t AIRPORT_CLOSED_block
Dummy block for indicating a closed airport.
Definition: airport.h:128
static const uint64_t HELIPAD2_block
Block belonging to helipad 2.
Definition: airport.h:96
static const uint64_t TERM6_block
Block belonging to terminal 6.
Definition: airport.h:94
static const uint64_t HELIPAD3_block
Block belonging to helipad 3.
Definition: airport.h:117
static const uint64_t HELIPAD1_block
Block belonging to helipad 1.
Definition: airport.h:95
AirportMovementStates
Movement States on Airports (headings target)
Definition: airport.h:60
@ HELITAKEOFF
Helicopter wants to leave the airport.
Definition: airport.h:74
@ TERM4
Heading for terminal 4.
Definition: airport.h:66
@ STARTTAKEOFF
Airplane has arrived at a runway for take-off.
Definition: airport.h:72
@ HELIPAD2
Heading for helipad 2.
Definition: airport.h:70
@ ENDTAKEOFF
Airplane has reached end-point of the take-off runway.
Definition: airport.h:73
@ TERM5
Heading for terminal 5.
Definition: airport.h:67
@ TERM6
Heading for terminal 6.
Definition: airport.h:68
@ TERM3
Heading for terminal 3.
Definition: airport.h:65
@ TERM8
Heading for terminal 8.
Definition: airport.h:81
@ HELIPAD3
Heading for helipad 3.
Definition: airport.h:82
@ HELIPAD1
Heading for helipad 1.
Definition: airport.h:69
@ TERM2
Heading for terminal 2.
Definition: airport.h:64
@ HANGAR
Heading for hangar.
Definition: airport.h:62
@ FLYING
Vehicle is flying in the air.
Definition: airport.h:75
@ TAKEOFF
Airplane wants to leave the airport.
Definition: airport.h:71
@ HELILANDING
Helicopter wants to land.
Definition: airport.h:78
@ TO_ALL
Go in this direction for every target.
Definition: airport.h:61
@ ENDLANDING
Airplane wants to finish landing.
Definition: airport.h:77
@ HELIENDLANDING
Helicopter wants to finish landing.
Definition: airport.h:79
@ TERM1
Heading for terminal 1.
Definition: airport.h:63
@ LANDING
Airplane wants to land.
Definition: airport.h:76
@ TERM7
Heading for terminal 7.
Definition: airport.h:80
@ TERMGROUP
Aircraft is looking for a free terminal in a terminalgroup.
Definition: airport.h:84
Class for backupping variables and making sure they are restored later.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
#define CLRBITS(x, y)
Clears several bits in a variable.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
#define SETBITS(x, y)
Sets several bits in a variable.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:22
bool IsValidCargoID(CargoID t)
Test whether cargo type is not INVALID_CARGO.
Definition: cargo_type.h:107
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:243
Common return value for all commands.
Definition: command_type.h:23
Money GetCost() const
The costs as made up to this moment.
Definition: command_type.h:83
bool Failed() const
Did this command fail?
Definition: command_type.h:171
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
Definition: game_core.cpp:146
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 constexpr int DAYS_IN_YEAR
days per year
static Date date
Current date in days (day counter).
uint Truncate(uint max_move=UINT_MAX)
Truncates the cargo in this list to the given amount.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
Definition: command_func.h:28
DoCommandFlag
List of flags for a command.
Definition: command_type.h:374
@ DC_EXEC
execute the given command
Definition: command_type.h:376
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.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
Subtract money from a company, including the money fraction.
Functions related to companies.
@ OWNER_NONE
The tile has no ownership.
Definition: company_type.h:25
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
DirDiff DirDifference(Direction d0, Direction d1)
Calculate the difference between two directions.
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection ChangeDiagDir(DiagDirection d, DiagDirDiff delta)
Applies a difference on a DiagDirection.
DiagDirDiff DiagDirDifference(DiagDirection d0, DiagDirection d1)
Calculate the difference between two DiagDirection values.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Direction
Defines the 8 directions on the map.
@ DIR_N
North.
@ DIR_SE
Southeast.
@ DIR_NE
Northeast.
@ DIR_W
West.
@ DIR_E
East.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_SW
Southwest.
DirDiff
Allow incrementing of Direction variables.
@ DIRDIFF_45LEFT
Angle of 45 degrees left.
@ DIRDIFF_REVERSE
One direction is the opposite of the other one.
@ DIRDIFF_45RIGHT
Angle of 45 degrees right.
@ DIRDIFF_SAME
Both directions faces to the same direction.
All disaster vehicles.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition: economy.cpp:969
@ EXPENSES_AIRCRAFT_RUN
Running costs aircraft.
Definition: economy_type.h:177
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.
uint16_t EngineID
Unique identification number of an engine.
Definition: engine_type.h:21
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
Definition: engine_type.h:95
@ ENGINE_EXCLUSIVE_PREVIEW
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
Definition: engine_type.h:184
Error reporting related functions.
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:21
Types for recording game performance data.
@ PFE_GL_AIRCRAFT
Time spent processing aircraft.
Base functions for all Games.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition: gfx_type.h:18
uint32_t PaletteID
The number of the palette.
Definition: gfx_type.h:19
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition: landscape.cpp:303
Functions related to OTTD's landscape.
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition: map.cpp:163
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:415
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition: map_func.h:404
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.
Definition: math_func.hpp:320
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.
@ AAT_STATION_AIRPLANE_LAND
Triggered when an airplane (not a helicopter) touches down at the airport (for single tile).
Functions for NewGRF engines.
@ PROP_AIRCRAFT_RANGE
Aircraft range.
@ PROP_AIRCRAFT_RUNNING_COST_FACTOR
Yearly runningcost.
@ PROP_AIRCRAFT_SPEED
Max. speed: 1 unit = 8 mph = 12.8 km-ish/h.
@ PROP_AIRCRAFT_CARGO_AGE_PERIOD
Number of ticks before carried cargo is aged.
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event, bool force)
Checks whether a NewGRF wants to play a different vehicle sound effect.
Functions related to NewGRF provided sounds.
@ VSE_TOUCHDOWN
Whenever a plane touches down.
Definition: newgrf_sound.h:23
@ VSE_START
Vehicle starting, i.e. leaving, the station.
Definition: newgrf_sound.h:19
Functions related to news.
void DeleteVehicleNews(VehicleID vid, StringID news)
Delete a news item type about a vehicle.
Definition: news_gui.cpp:930
void AddVehicleAdviceNewsItem(StringID string, VehicleID vehicle)
Adds a vehicle-advice news item.
Definition: news_func.h:40
void AddVehicleNewsItem(StringID string, NewsType type, VehicleID vehicle, StationID station=INVALID_STATION)
Adds a newsitem referencing a vehicle.
Definition: news_func.h:30
NewsType
Type of news.
Definition: news_type.h:23
@ NT_ACCIDENT_OTHER
An accident or disaster has occurred.
Definition: news_type.h:27
@ NT_ARRIVAL_COMPANY
First vehicle arrived for company.
Definition: news_type.h:24
@ NT_ARRIVAL_OTHER
First vehicle arrived for competitor.
Definition: news_type.h:25
@ NT_ACCIDENT
An accident or disaster has occurred.
Definition: news_type.h:26
bool ProcessOrders(Vehicle *v)
Handle the orders of a vehicle and determine the next place to go to if needed.
Definition: order_cmd.cpp:2127
void CheckOrders(const Vehicle *v)
Check the orders of a vehicle, to see if there are invalid orders and stuff.
Definition: order_cmd.cpp:1711
void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination, bool hangar)
Removes an order from all vehicles.
Definition: order_cmd.cpp:1787
@ ODATFB_NEAREST_DEPOT
Send the vehicle to the nearest depot.
Definition: order_type.h:105
@ ODTFB_PART_OF_ORDERS
This depot order is because of a regular order.
Definition: order_type.h:96
@ ODTFB_SERVICE
This depot order is because of the servicing limit.
Definition: order_type.h:95
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:57
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
Functions related to sound.
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition: sound_type.h:116
@ SND_18_TAKEOFF_HELICOPTER
22 == 0x16 Takeoff: helicopter
Definition: sound_type.h:61
@ SND_17_SKID_PLANE
21 == 0x15 Plane landing / touching ground
Definition: sound_type.h:60
@ SND_12_EXPLOSION
16 == 0x10 Destruction, crashes, disasters, ...
Definition: sound_type.h:55
Functions to cache sprites in memory.
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition: sprites.h:1605
Base classes/functions for stations.
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition: station_map.h:28
@ HVOT_AIRCRAFT
Station has seen an aircraft.
Definition: station_type.h:69
@ FACIL_AIRPORT
Station with an airport.
Definition: station_type.h:57
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:280
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
uint32_t cached_max_range_sqr
Cached squared maximum range.
Definition: aircraft.h:65
uint16_t cached_max_range
Cached maximum range.
Definition: aircraft.h:66
Information about a aircraft vehicle.
Definition: engine_type.h:100
uint16_t max_speed
Maximum speed (1 unit = 8 mph = 12.8 km-ish/h)
Definition: engine_type.h:107
uint8_t mail_capacity
Mail capacity (bags).
Definition: engine_type.h:108
uint8_t subtype
Type of aircraft.
Definition: engine_type.h:104
uint16_t passenger_capacity
Passenger capacity (persons).
Definition: engine_type.h:109
uint16_t max_range
Maximum range of this aircraft.
Definition: engine_type.h:110
Aircraft, helicopters, rotors and their shadows belong to this class.
Definition: aircraft.h:72
bool Tick() override
Calls the tick handler of the vehicle.
uint8_t pos
Next desired position of the aircraft.
Definition: aircraft.h:74
uint8_t state
State of the airport.
Definition: aircraft.h:77
uint8_t flags
Aircraft flags.
Definition: aircraft.h:81
Money GetRunningCost() const override
Gets the running cost of a vehicle.
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
uint8_t number_consecutive_turns
Protection to prevent the aircraft of making a lot of turns in order to reach a specific point.
Definition: aircraft.h:79
void OnNewEconomyDay() override
Economy day handler.
uint Crash(bool flooded=false) override
Crash the (whole) vehicle chain.
void OnNewCalendarDay() override
Calendar day handler.
uint8_t turn_counter
Ticks between each turn to prevent > 45 degree turns.
Definition: aircraft.h:80
bool IsNormalAircraft() const
Check if the aircraft type is a normal flying device; eg not a rotor or a shadow.
Definition: aircraft.h:121
void MarkDirty() override
Marks the vehicles to be redrawn and updates cached variables.
uint8_t previous_pos
Previous desired position of the aircraft.
Definition: aircraft.h:75
StationID targetairport
Airport to go to next.
Definition: aircraft.h:76
ClosestDepot FindClosestDepot() override
Find the closest depot for this vehicle and tell us the location, DestinationID and whether we should...
uint16_t crashed_counter
Timer for handling crash animations.
Definition: aircraft.h:73
Finite sTate mAchine (FTA) of an airport.
Definition: airport.h:143
struct AirportFTA * layout
state machine for airport
Definition: airport.h:177
const uint8_t num_helipads
Number of helipads on this airport. When 0 helicopters will go to normal terminals.
Definition: airport.h:179
uint8_t delta_z
Z adjustment for helicopter pads.
Definition: airport.h:183
const uint8_t * terminals
Array with the number of terminal groups, followed by the number of terminals in each group.
Definition: airport.h:178
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:182
uint8_t nofelements
number of positions the airport consists of
Definition: airport.h:181
const AirportMovingData * MovingData(uint8_t position) const
Get movement data at a position.
Definition: airport.h:170
Flags flags
Flags for this airport type.
Definition: airport.h:180
@ AIRPLANES
Can planes land on this airport type?
Definition: airport.h:147
@ SHORT_STRIP
This airport has a short landing strip, dangerous for fast aircraft.
Definition: airport.h:150
Internal structure used in openttd - Finite sTate mAchine --> FTA.
Definition: airport.h:190
AirportFTA * next
possible extra movement choices from this position
Definition: airport.h:191
uint8_t heading
heading (current orders), guiding an airplane to its target on an airport
Definition: airport.h:195
uint64_t block
64 bit blocks (st->airport.flags), should be enough for the most complex airports
Definition: airport.h:192
uint8_t position
the position that an airplane is at
Definition: airport.h:193
uint8_t next_position
next position from this position
Definition: airport.h:194
A single location on an airport where aircraft can move to.
Definition: airport.h:131
uint16_t flag
special flags when moving towards the destination.
Definition: airport.h:134
int16_t x
x-coordinate of the destination.
Definition: airport.h:132
int16_t y
y-coordinate of the destination.
Definition: airport.h:133
Direction direction
Direction to turn the aircraft after reaching the destination.
Definition: airport.h:135
bool HasHangar() const
Check if this airport has at least one hangar.
Definition: station_base.h:323
uint GetHangarNum(TileIndex tile) const
Get the hangar number of the hangar at a specific tile.
Definition: station_base.h:387
Direction rotation
How this airport is rotated.
Definition: station_base.h:296
uint8_t type
Type of this airport,.
Definition: station_base.h:294
Direction GetHangarExitDirection(TileIndex tile) const
Get the exit direction of the hangar at a specific tile.
Definition: station_base.h:374
uint64_t flags
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
Definition: station_base.h:293
const AirportFTAClass * GetFTA() const
Get the finite-state machine for this airport or the finite-state machine for the dummy airport in ca...
Definition: station_base.h:317
TileIndex GetHangarTile(uint hangar_num) const
Get the first tile of the given hangar.
Definition: station_base.h:358
Class to backup a specific variable and restore it later.
Definition: backup_type.hpp:21
std::string name
Name of vehicle.
Definition: base_consist.h:18
TimerGameTick::Ticks current_order_time
How many ticks have passed since this order started.
Definition: base_consist.h:21
uint16_t vehicle_flags
Used for gradual loading and other miscellaneous things (.
Definition: base_consist.h:34
TileIndex xy
Base tile of the station.
Owner owner
The owner of this station.
VehicleType type
Type of vehicle.
Definition: vehicle_type.h:51
bool value
tells if the bool cheat is active or not
Definition: cheat_type.h:18
Cheat no_jetcrash
no jet will crash on small airports anymore
Definition: cheat_type.h:31
SoundSettings sound
sound effect settings
Structure to return information about the closest depot location, and whether it could be found.
Definition: vehicle_base.h:230
Disasters, like submarines, skyrangers and their shadows, belong to this class.
uint16_t cargo_age_period
Number of ticks before carried cargo is aged.
Definition: engine_type.h:159
CargoID GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition: engine_base.h:96
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition: engine_base.h:42
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition: engine.cpp:201
uint8_t original_image_index
Original vehicle image index, thus the image index of the overridden vehicle.
Definition: engine_base.h:55
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
Definition: engine_base.h:167
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition: engine.cpp:443
uint16_t reliability
Current reliability of the engine.
Definition: engine_base.h:41
uint8_t flags
Flags of the engine.
Definition: engine_base.h:49
VehicleSettings vehicle
options for vehicles
OrderSettings order
settings related to orders
Position information of a vehicle after it moved.
Definition: vehicle_func.h:77
TileIndex new_tile
Tile of the vehicle after moving.
Definition: vehicle_func.h:80
int y
x and y position of the vehicle after moving
Definition: vehicle_func.h:78
TileIndex old_tile
Current tile of the vehicle.
Definition: vehicle_func.h:79
Stores station stats for a single cargo.
Definition: station_base.h:166
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Definition: station_base.h:210
uint8_t rating
Station rating for this cargo.
Definition: station_base.h:226
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition: map_func.h:306
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition: map_func.h:297
Combination of aircraft state for going to a certain terminal and the airport flag for that terminal ...
uint64_t airport_flag
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.
Definition: vehicle_base.h:200
bool serviceathelipad
service helicopters at helipads automatically (no need to send to depot)
OrderDepotTypeFlags GetDepotOrderType() const
What caused us going to the depot?
Definition: order_base.h:144
DestinationID GetDestination() const
Gets the destination of this order.
Definition: order_base.h:103
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition: order_base.h:70
OrderType GetType() const
Get the type of order of this order.
Definition: order_base.h:76
void MakeDummy()
Makes this order a Dummy order.
Definition: order_cmd.cpp:133
void MakeGoToDepot(DepotID destination, OrderDepotTypeFlags order, OrderNonStopFlags non_stop_type=ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS, OrderDepotActionFlags action=ODATF_SERVICE_ONLY, CargoID cargo=CARGO_NO_REFIT)
Makes this order a Go To Depot order.
Definition: order_cmd.cpp:90
OrderDepotActionFlags GetDepotActionType() const
What are we going to do when in the depot.
Definition: order_base.h:146
void Free()
'Free' the order
Definition: order_cmd.cpp:63
uint16_t w
The width of the area.
Definition: tilearea_type.h:20
TileIndex tile
The base tile of the area.
Definition: tilearea_type.h:19
uint16_t h
The height of the area.
Definition: tilearea_type.h:21
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
Specification of a rectangle with absolute coordinates of all edges.
int Width() const
Get width of Rect.
int Height() const
Get height of Rect.
Titem value
Value of current item.
bool disaster
Play disaster and accident sounds.
static Station * GetIfValid(size_t index)
Returns station if the index is a valid index for this station type.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
static Station * Get(size_t index)
Gets station with given index.
static Station * GetByTile(TileIndex tile)
Get the station belonging to a specific tile.
static Aircraft * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
T * Next() const
Get next vehicle in the chain.
void UpdateViewport(bool force_update, bool update_delta)
Update vehicle sprite- and position caches.
static Pool::IterateWrapper< Aircraft > Iterate(size_t from=0)
Returns an iterable ensemble of all valid vehicles of type T.
Station data structure.
Definition: station_base.h:439
GoodsEntry goods[NUM_CARGO]
Goods at this station.
Definition: station_base.h:468
Airport airport
Tile area the airport covers.
Definition: station_base.h:453
uint16_t cached_cargo_age_period
Number of ticks before carried cargo is aged.
Definition: vehicle_base.h:127
uint16_t cached_max_speed
Maximum speed of the consist (minimum of the max speed of all vehicles in the consist).
Definition: vehicle_base.h:126
uint8_t plane_crashes
number of plane crashes, 0 = none, 1 = reduced, 2 = normal
uint8_t plane_speed
divisor for speed of aircraft
Sprite sequence for a vehicle part.
Definition: vehicle_base.h:135
bool IsValid() const
Check whether the sequence contains any sprites.
Definition: vehicle_base.h:152
void GetBounds(Rect *bounds) const
Determine shared bounds of all sprites.
Definition: vehicle.cpp:103
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
Definition: vehicle_base.h:168
void CopyWithoutPalette(const VehicleSpriteSeq &src)
Copy data from another sprite sequence, while dropping all recolouring information.
Definition: vehicle_base.h:178
void Draw(int x, int y, PaletteID default_pal, bool force_pal) const
Draw the sprite sequence.
Definition: vehicle.cpp:131
Vehicle data structure.
Definition: vehicle_base.h:244
EngineID engine_type
The type of engine used for this vehicle.
Definition: vehicle_base.h:323
int32_t z_pos
z coordinate.
Definition: vehicle_base.h:306
Direction direction
facing
Definition: vehicle_base.h:307
StationIDStack GetNextStoppingStation() const
Get the next station the vehicle will stop at.
Definition: vehicle_base.h:750
const Engine * GetEngine() const
Retrieves the engine of the vehicle.
Definition: vehicle.cpp:747
virtual uint Crash(bool flooded=false)
Crash the (whole) vehicle chain.
Definition: vehicle.cpp:280
virtual bool IsChainInDepot() const
Check whether the whole vehicle chain is in the depot.
Definition: vehicle_base.h:554
VehicleCargoList cargo
The cargo this vehicle is carrying.
Definition: vehicle_base.h:341
uint8_t x_extent
x-extent of vehicle bounding box
Definition: vehicle_base.h:316
TimerGameEconomy::Date date_of_last_service
Last economy date the vehicle had a service at a depot.
Definition: vehicle_base.h:295
uint16_t cargo_cap
total capacity
Definition: vehicle_base.h:344
StationID last_loading_station
Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.
Definition: vehicle_base.h:338
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
Definition: vehicle_base.h:355
uint16_t random_bits
Bits used for randomized variational spritegroups.
Definition: vehicle_base.h:335
uint8_t day_counter
Increased by one for each day.
Definition: vehicle_base.h:349
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:2430
Money profit_this_year
Profit this year << 8, low 8 bits are fract.
Definition: vehicle_base.h:273
SpriteID colourmap
NOSAVE: cached colour mapping.
Definition: vehicle_base.h:288
uint8_t breakdown_ctr
Counter for managing breakdown events.
Definition: vehicle_base.h:299
uint GetOldAdvanceSpeed(uint speed)
Determines the effective direction-specific vehicle movement speed.
Definition: vehicle_base.h:428
uint8_t z_extent
z-extent of vehicle bounding box
Definition: vehicle_base.h:318
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...
Definition: vehicle_base.h:296
uint8_t subspeed
fractional speed
Definition: vehicle_base.h:329
void LeaveUnbunchingDepot()
Leave an unbunching depot and calculate the next departure time for shared order vehicles.
Definition: vehicle.cpp:2514
int8_t y_offs
y offset for vehicle sprite
Definition: vehicle_base.h:322
Vehicle * Next() const
Get the next vehicle of this vehicle.
Definition: vehicle_base.h:632
uint8_t acceleration
used by train & aircraft
Definition: vehicle_base.h:330
Order current_order
The current order (+ status, like: loading)
Definition: vehicle_base.h:356
CargoID cargo_type
type of cargo this vehicle is carrying
Definition: vehicle_base.h:342
int8_t x_offs
x offset for vehicle sprite
Definition: vehicle_base.h:321
int32_t y_pos
y coordinate.
Definition: vehicle_base.h:305
int32_t x_pos
x coordinate.
Definition: vehicle_base.h:304
uint8_t y_extent
y-extent of vehicle bounding box
Definition: vehicle_base.h:317
uint16_t refit_cap
Capacity left over from before last refit.
Definition: vehicle_base.h:345
uint8_t vehstatus
Status.
Definition: vehicle_base.h:354
VehicleCache vcache
Cache of often used vehicle values.
Definition: vehicle_base.h:364
void BeginLoading()
Prepare everything to begin the loading when arriving at a station.
Definition: vehicle.cpp:2203
uint8_t spritenum
currently displayed sprite index 0xfd == custom sprite, 0xfe == custom second head sprite 0xff == res...
Definition: vehicle_base.h:315
uint16_t cur_speed
current speed
Definition: vehicle_base.h:328
bool IsWaitingForUnbunching() const
Check whether a vehicle inside a depot is waiting for unbunching.
Definition: vehicle.cpp:2561
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition: vehicle.cpp:2937
uint8_t breakdowns_since_last_service
Counter for the amount of breakdowns.
Definition: vehicle_base.h:301
TimerGameCalendar::Date max_age
Maximum age.
Definition: vehicle_base.h:294
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
Definition: vehicle_base.h:368
uint16_t reliability
Reliability.
Definition: vehicle_base.h:297
bool HandleBreakdown()
Handle all of the aspects of a vehicle breakdown This includes adding smoke and sounds,...
Definition: vehicle.cpp:1363
uint8_t progress
The percentage (if divided by 256) this vehicle already crossed the tile unit.
Definition: vehicle_base.h:332
void UpdatePositionAndViewport()
Update the position of the vehicle, and update the viewport.
Definition: vehicle.cpp:1764
uint16_t reliability_spd_dec
Reliability decrease speed.
Definition: vehicle_base.h:298
uint8_t tick_counter
Increased by one for each tick.
Definition: vehicle_base.h:350
TileIndex tile
Current tile index.
Definition: vehicle_base.h:264
void UpdatePosition()
Update the position of the vehicle.
Definition: vehicle.cpp:1693
StationID last_station_visited
The last station we stopped at.
Definition: vehicle_base.h:337
void InvalidateNewGRFCacheOfChain()
Invalidates cached NewGRF variables of all vehicles in the chain (after the current vehicle)
Definition: vehicle_base.h:504
TimerGameCalendar::Year build_year
Year the vehicle has been built.
Definition: vehicle_base.h:291
Owner owner
Which company owns the vehicle?
Definition: vehicle_base.h:309
bool NeedsAutomaticServicing() const
Checks if the current order should be interrupted for a service-in-depot order.
Definition: vehicle.cpp:272
uint8_t running_ticks
Number of ticks this vehicle was not stopped this day.
Definition: vehicle_base.h:351
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition: tile_map.h:312
static debug_inline uint TileHeight(Tile tile)
Returns the height of a tile.
Definition: tile_map.h:29
static const uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in #ZOOM_BASE.
Definition: tile_type.h:18
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:95
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:1552
void VehicleServiceInDepot(Vehicle *v)
Service a vehicle and all subsequent vehicles in the consist.
Definition: vehicle.cpp:167
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition: vehicle.cpp:1784
void DecreaseVehicleValue(Vehicle *v)
Decrease the value of a vehicle.
Definition: vehicle.cpp:1301
void EconomyAgeVehicle(Vehicle *v)
Update economy age of a vehicle.
Definition: vehicle.cpp:1429
bool CanVehicleUseStation(EngineID engine_type, const Station *st)
Can this station be used by the given engine type?
Definition: vehicle.cpp:3058
void AgeVehicle(Vehicle *v)
Update age of a vehicle.
Definition: vehicle.cpp:1441
@ VF_BUILT_AS_PROTOTYPE
Vehicle is a prototype (accepted as exclusive preview).
Definition: vehicle_base.h:47
@ VS_UNCLICKABLE
Vehicle is not clickable by the user (shadow vehicles).
Definition: vehicle_base.h:35
@ VS_AIRCRAFT_BROKEN
Aircraft is broken down.
Definition: vehicle_base.h:39
@ VS_SHADOW
Vehicle is a shadow vehicle.
Definition: vehicle_base.h:38
@ VS_STOPPED
Vehicle is stopped by the player.
Definition: vehicle_base.h:34
@ VS_HIDDEN
Vehicle is not visible.
Definition: vehicle_base.h:33
@ VS_CRASHED
Vehicle is crashed.
Definition: vehicle_base.h:40
@ VS_DEFPAL
Use default vehicle palette.
Definition: vehicle_base.h:36
Command definitions for vehicles.
Functions related to vehicles.
EngineImageType
Visualisation contexts of vehicles and engines.
Definition: vehicle_type.h:78
@ EIT_ON_MAP
Vehicle drawn in viewport.
Definition: vehicle_type.h:79
@ VEH_AIRCRAFT
Aircraft vehicle type.
Definition: vehicle_type.h:27
@ LocateHangar
Find another airport if the target one lacks a hangar.
@ None
No special flags.
@ Service
The vehicle will leave the depot right after arrival (service only)
@ WID_VV_START_STOP
Start or stop this vehicle, and show information about the current state.
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting)
Definition: window.cpp:3119
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:3211
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:3106
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
Window functions not directly related to making/drawing windows.
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
Definition: window_type.h:351
@ WC_VEHICLE_DETAILS
Vehicle details; Window numbers:
Definition: window_type.h:200
@ WC_VEHICLE_VIEW
Vehicle view; Window numbers:
Definition: window_type.h:339
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Definition: window_type.h:326
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