aircraft_cmd.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00015 #include "stdafx.h"
00016 #include "aircraft.h"
00017 #include "landscape.h"
00018 #include "news_func.h"
00019 #include "vehicle_gui.h"
00020 #include "newgrf_engine.h"
00021 #include "newgrf_sound.h"
00022 #include "spritecache.h"
00023 #include "strings_func.h"
00024 #include "command_func.h"
00025 #include "window_func.h"
00026 #include "date_func.h"
00027 #include "vehicle_func.h"
00028 #include "sound_func.h"
00029 #include "cheat_type.h"
00030 #include "company_base.h"
00031 #include "ai/ai.hpp"
00032 #include "company_func.h"
00033 #include "effectvehicle_func.h"
00034 #include "station_base.h"
00035 #include "engine_base.h"
00036 #include "core/random_func.hpp"
00037 #include "core/backup_type.hpp"
00038 #include "zoom_func.h"
00039 
00040 #include "table/strings.h"
00041 
00042 static const int ROTOR_Z_OFFSET         = 5;    
00043 
00044 static const int PLANE_HOLDING_ALTITUDE = 150;  
00045 static const int HELI_FLIGHT_ALTITUDE   = 184;  
00046 
00047 
00048 void Aircraft::UpdateDeltaXY(Direction direction)
00049 {
00050   this->x_offs = -1;
00051   this->y_offs = -1;
00052   this->x_extent = 2;
00053   this->y_extent = 2;
00054 
00055   switch (this->subtype) {
00056     default: NOT_REACHED();
00057 
00058     case AIR_AIRCRAFT:
00059     case AIR_HELICOPTER:
00060       switch (this->state) {
00061         default: break;
00062         case ENDTAKEOFF:
00063         case LANDING:
00064         case HELILANDING:
00065         case FLYING:
00066           this->x_extent = 24;
00067           this->y_extent = 24;
00068           break;
00069       }
00070       this->z_extent = 5;
00071       break;
00072 
00073     case AIR_SHADOW:
00074       this->z_extent = 1;
00075       this->x_offs = 0;
00076       this->y_offs = 0;
00077       break;
00078 
00079     case AIR_ROTOR:
00080       this->z_extent = 1;
00081       break;
00082   }
00083 }
00084 
00085 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
00086 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00087 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00088 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
00089 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
00090 static void CrashAirplane(Aircraft *v);
00091 
00092 static const SpriteID _aircraft_sprite[] = {
00093   0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
00094   0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
00095   0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
00096   0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
00097   0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
00098   0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
00099   0x0EBD, 0x0EC5
00100 };
00101 
00103 enum HelicopterRotorStates {
00104   HRS_ROTOR_STOPPED,
00105   HRS_ROTOR_MOVING_1,
00106   HRS_ROTOR_MOVING_2,
00107   HRS_ROTOR_MOVING_3,
00108 };
00109 
00117 static StationID FindNearestHangar(const Aircraft *v)
00118 {
00119   const Station *st;
00120   uint best = 0;
00121   StationID index = INVALID_STATION;
00122   TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
00123   const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00124 
00125   FOR_ALL_STATIONS(st) {
00126     if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00127 
00128     const AirportFTAClass *afc = st->airport.GetFTA();
00129     if (!st->airport.HasHangar() || (
00130           /* don't crash the plane if we know it can't land at the airport */
00131           (afc->flags & AirportFTAClass::SHORT_STRIP) &&
00132           (avi->subtype & AIR_FAST) &&
00133           !_cheats.no_jetcrash.value)) {
00134       continue;
00135     }
00136 
00137     /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
00138     uint distance = DistanceSquare(vtile, st->airport.tile);
00139     if (distance < best || index == INVALID_STATION) {
00140       best = distance;
00141       index = st->index;
00142     }
00143   }
00144   return index;
00145 }
00146 
00147 SpriteID Aircraft::GetImage(Direction direction, EngineImageType image_type) const
00148 {
00149   uint8 spritenum = this->spritenum;
00150 
00151   if (is_custom_sprite(spritenum)) {
00152     SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type);
00153     if (sprite != 0) return sprite;
00154 
00155     spritenum = this->GetEngine()->original_image_index;
00156   }
00157 
00158   return direction + _aircraft_sprite[spritenum];
00159 }
00160 
00161 SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type)
00162 {
00163   assert(v->subtype == AIR_HELICOPTER);
00164 
00165   const Aircraft *w = v->Next()->Next();
00166   if (is_custom_sprite(v->spritenum)) {
00167     SpriteID sprite = GetCustomRotorSprite(v, false, image_type);
00168     if (sprite != 0) return sprite;
00169   }
00170 
00171   /* Return standard rotor sprites if there are no custom sprites for this helicopter */
00172   return SPR_ROTOR_STOPPED + w->state;
00173 }
00174 
00175 static SpriteID GetAircraftIcon(EngineID engine, EngineImageType image_type)
00176 {
00177   const Engine *e = Engine::Get(engine);
00178   uint8 spritenum = e->u.air.image_index;
00179 
00180   if (is_custom_sprite(spritenum)) {
00181     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
00182     if (sprite != 0) return sprite;
00183 
00184     spritenum = e->original_image_index;
00185   }
00186 
00187   return DIR_W + _aircraft_sprite[spritenum];
00188 }
00189 
00190 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
00191 {
00192   SpriteID sprite = GetAircraftIcon(engine, image_type);
00193   const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00194   preferred_x = Clamp(preferred_x, left - UnScaleByZoom(real_sprite->x_offs, ZOOM_LVL_GUI), right - UnScaleByZoom(real_sprite->width, ZOOM_LVL_GUI) - UnScaleByZoom(real_sprite->x_offs, ZOOM_LVL_GUI));
00195   DrawSprite(sprite, pal, preferred_x, y);
00196 
00197   if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00198     SpriteID rotor_sprite = GetCustomRotorIcon(engine, image_type);
00199     if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00200     DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00201   }
00202 }
00203 
00210 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, EngineImageType image_type)
00211 {
00212   const Sprite *spr = GetSprite(GetAircraftIcon(engine, image_type), ST_NORMAL);
00213 
00214   width  = UnScaleByZoom(spr->width, ZOOM_LVL_GUI);
00215   height = UnScaleByZoom(spr->height, ZOOM_LVL_GUI);
00216 }
00217 
00227 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
00228 {
00229   const AircraftVehicleInfo *avi = &e->u.air;
00230   const Station *st = Station::GetByTile(tile);
00231 
00232   /* Prevent building aircraft types at places which can't handle them */
00233   if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
00234 
00235   /* Make sure all aircraft end up in the first tile of the hanger. */
00236   tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
00237 
00238   if (flags & DC_EXEC) {
00239     Aircraft *v = new Aircraft(); // aircraft
00240     Aircraft *u = new Aircraft(); // shadow
00241     *ret = v;
00242 
00243     v->direction = DIR_SE;
00244 
00245     v->owner = u->owner = _current_company;
00246 
00247     v->tile = tile;
00248 
00249     uint x = TileX(tile) * TILE_SIZE + 5;
00250     uint y = TileY(tile) * TILE_SIZE + 3;
00251 
00252     v->x_pos = u->x_pos = x;
00253     v->y_pos = u->y_pos = y;
00254 
00255     u->z_pos = GetSlopePixelZ(x, y);
00256     v->z_pos = u->z_pos + 1;
00257 
00258     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00259     u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00260 
00261     v->spritenum = avi->image_index;
00262 
00263     v->cargo_cap = avi->passenger_capacity;
00264     u->cargo_cap = avi->mail_capacity;
00265 
00266     v->cargo_type = e->GetDefaultCargoType();
00267     u->cargo_type = CT_MAIL;
00268 
00269     v->name = NULL;
00270     v->last_station_visited = INVALID_STATION;
00271     v->last_loading_station = INVALID_STATION;
00272 
00273     v->acceleration = avi->acceleration;
00274     v->engine_type = e->index;
00275     u->engine_type = e->index;
00276 
00277     v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00278     v->UpdateDeltaXY(INVALID_DIR);
00279 
00280     u->subtype = AIR_SHADOW;
00281     u->UpdateDeltaXY(INVALID_DIR);
00282 
00283     v->reliability = e->reliability;
00284     v->reliability_spd_dec = e->reliability_spd_dec;
00285     v->max_age = e->GetLifeLengthInDays();
00286 
00287     _new_vehicle_id = v->index;
00288 
00289     v->pos = GetVehiclePosOnBuild(tile);
00290 
00291     v->state = HANGAR;
00292     v->previous_pos = v->pos;
00293     v->targetairport = GetStationIndex(tile);
00294     v->SetNext(u);
00295 
00296     v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_aircraft;
00297 
00298     v->date_of_last_service = _date;
00299     v->build_year = u->build_year = _cur_year;
00300 
00301     v->cur_image = u->cur_image = SPR_IMG_QUERY;
00302 
00303     v->random_bits = VehicleRandomBits();
00304     u->random_bits = VehicleRandomBits();
00305 
00306     v->vehicle_flags = 0;
00307     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00308 
00309     v->InvalidateNewGRFCacheOfChain();
00310 
00311     v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
00312 
00313     v->InvalidateNewGRFCacheOfChain();
00314 
00315     UpdateAircraftCache(v);
00316 
00317     VehicleMove(v, false);
00318     VehicleMove(u, false);
00319 
00320     /* Aircraft with 3 vehicles (chopper)? */
00321     if (v->subtype == AIR_HELICOPTER) {
00322       Aircraft *w = new Aircraft();
00323       w->engine_type = e->index;
00324       w->direction = DIR_N;
00325       w->owner = _current_company;
00326       w->x_pos = v->x_pos;
00327       w->y_pos = v->y_pos;
00328       w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
00329       w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00330       w->spritenum = 0xFF;
00331       w->subtype = AIR_ROTOR;
00332       w->cur_image = SPR_ROTOR_STOPPED;
00333       w->random_bits = VehicleRandomBits();
00334       /* Use rotor's air.state to store the rotor animation frame */
00335       w->state = HRS_ROTOR_STOPPED;
00336       w->UpdateDeltaXY(INVALID_DIR);
00337 
00338       u->SetNext(w);
00339       VehicleMove(w, false);
00340     }
00341   }
00342 
00343   return CommandCost();
00344 }
00345 
00346 
00347 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00348 {
00349   const Station *st = GetTargetAirportIfValid(this);
00350   /* If the station is not a valid airport or if it has no hangars */
00351   if (st == NULL || !st->airport.HasHangar()) {
00352     /* the aircraft has to search for a hangar on its own */
00353     StationID station = FindNearestHangar(this);
00354 
00355     if (station == INVALID_STATION) return false;
00356 
00357     st = Station::Get(station);
00358   }
00359 
00360   if (location    != NULL) *location    = st->xy;
00361   if (destination != NULL) *destination = st->index;
00362 
00363   return true;
00364 }
00365 
00366 static void CheckIfAircraftNeedsService(Aircraft *v)
00367 {
00368   if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00369   if (v->IsInDepot()) {
00370     VehicleServiceInDepot(v);
00371     return;
00372   }
00373 
00374   /* When we're parsing conditional orders and the like
00375    * we don't want to consider going to a depot too. */
00376   if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
00377 
00378   const Station *st = Station::Get(v->current_order.GetDestination());
00379 
00380   assert(st != NULL);
00381 
00382   /* only goto depot if the target airport has a depot */
00383   if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
00384     v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00385     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00386   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00387     v->current_order.MakeDummy();
00388     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00389   }
00390 }
00391 
00392 Money Aircraft::GetRunningCost() const
00393 {
00394   const Engine *e = this->GetEngine();
00395   uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00396   return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
00397 }
00398 
00399 void Aircraft::OnNewDay()
00400 {
00401   if (!this->IsNormalAircraft()) return;
00402 
00403   if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00404 
00405   CheckOrders(this);
00406 
00407   CheckVehicleBreakdown(this);
00408   AgeVehicle(this);
00409   CheckIfAircraftNeedsService(this);
00410 
00411   if (this->running_ticks == 0) return;
00412 
00413   CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00414 
00415   this->profit_this_year -= cost.GetCost();
00416   this->running_ticks = 0;
00417 
00418   SubtractMoneyFromCompanyFract(this->owner, cost);
00419 
00420   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00421   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00422 }
00423 
00424 static void HelicopterTickHandler(Aircraft *v)
00425 {
00426   Aircraft *u = v->Next()->Next();
00427 
00428   if (u->vehstatus & VS_HIDDEN) return;
00429 
00430   /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
00431    * loading/unloading at a terminal or stopped */
00432   if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00433     if (u->cur_speed != 0) {
00434       u->cur_speed++;
00435       if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00436         u->cur_speed = 0;
00437       }
00438     }
00439   } else {
00440     if (u->cur_speed == 0) {
00441       u->cur_speed = 0x70;
00442     }
00443     if (u->cur_speed >= 0x50) {
00444       u->cur_speed--;
00445     }
00446   }
00447 
00448   int tick = ++u->tick_counter;
00449   int spd = u->cur_speed >> 4;
00450 
00451   SpriteID img;
00452   if (spd == 0) {
00453     u->state = HRS_ROTOR_STOPPED;
00454     img = GetRotorImage(v, EIT_ON_MAP);
00455     if (u->cur_image == img) return;
00456   } else if (tick >= spd) {
00457     u->tick_counter = 0;
00458     u->state++;
00459     if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00460     img = GetRotorImage(v, EIT_ON_MAP);
00461   } else {
00462     return;
00463   }
00464 
00465   u->cur_image = img;
00466 
00467   VehicleMove(u, true);
00468 }
00469 
00477 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00478 {
00479   v->x_pos = x;
00480   v->y_pos = y;
00481   v->z_pos = z;
00482 
00483   v->UpdateViewport(true, false);
00484   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
00485 
00486   Aircraft *u = v->Next();
00487 
00488   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00489   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00490   u->x_pos = x;
00491   u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
00492 
00493   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00494   u->z_pos = GetSlopePixelZ(safe_x, safe_y);
00495   u->cur_image = v->cur_image;
00496 
00497   VehicleMove(u, true);
00498 
00499   u = u->Next();
00500   if (u != NULL) {
00501     u->x_pos = x;
00502     u->y_pos = y;
00503     u->z_pos = z + ROTOR_Z_OFFSET;
00504 
00505     VehicleMove(u, true);
00506   }
00507 }
00508 
00513 void HandleAircraftEnterHangar(Aircraft *v)
00514 {
00515   v->subspeed = 0;
00516   v->progress = 0;
00517 
00518   Aircraft *u = v->Next();
00519   u->vehstatus |= VS_HIDDEN;
00520   u = u->Next();
00521   if (u != NULL) {
00522     u->vehstatus |= VS_HIDDEN;
00523     u->cur_speed = 0;
00524   }
00525 
00526   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00527 }
00528 
00529 static void PlayAircraftSound(const Vehicle *v)
00530 {
00531   if (!PlayVehicleSound(v, VSE_START)) {
00532     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00533   }
00534 }
00535 
00536 
00542 void UpdateAircraftCache(Aircraft *v)
00543 {
00544   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00545   if (max_speed != 0) {
00546     /* Convert from original units to km-ish/h */
00547     max_speed = (max_speed * 128) / 10;
00548 
00549     v->vcache.cached_max_speed = max_speed;
00550   } else {
00551     /* Use the default max speed of the vehicle. */
00552     v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00553   }
00554 
00555   /* Update cargo aging period. */
00556   v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00557   Aircraft *u = v->Next(); // Shadow for mail
00558   u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00559 }
00560 
00561 
00565 enum AircraftSpeedLimits {
00566   SPEED_LIMIT_TAXI     =     50,  
00567   SPEED_LIMIT_APPROACH =    230,  
00568   SPEED_LIMIT_BROKEN   =    320,  
00569   SPEED_LIMIT_HOLD     =    425,  
00570   SPEED_LIMIT_NONE     = 0xFFFF   
00571 };
00572 
00580 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00581 {
00582   uint spd = v->acceleration * 16;
00583   byte t;
00584 
00585   /* Adjust speed limits by plane speed factor to prevent taxiing
00586    * and take-off speeds being too low. */
00587   speed_limit *= _settings_game.vehicle.plane_speed;
00588 
00589   if (v->vcache.cached_max_speed < speed_limit) {
00590     if (v->cur_speed < speed_limit) hard_limit = false;
00591     speed_limit = v->vcache.cached_max_speed;
00592   }
00593 
00594   v->subspeed = (t = v->subspeed) + (byte)spd;
00595 
00596   /* Aircraft's current speed is used twice so that very fast planes are
00597    * forced to slow down rapidly in the short distance needed. The magic
00598    * value 16384 was determined to give similar results to the old speed/48
00599    * method at slower speeds. This also results in less reduction at slow
00600    * speeds to that aircraft do not get to taxi speed straight after
00601    * touchdown. */
00602   if (!hard_limit && v->cur_speed > speed_limit) {
00603     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00604   }
00605 
00606   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00607 
00608   /* adjust speed for broken vehicles */
00609   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00610 
00611   /* updates statusbar only if speed have changed to save CPU time */
00612   if (spd != v->cur_speed) {
00613     v->cur_speed = spd;
00614     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00615   }
00616 
00617   /* Adjust distance moved by plane speed setting */
00618   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00619 
00620   /* Convert direction-indepenent speed into direction-dependent speed. (old movement method) */
00621   spd = v->GetOldAdvanceSpeed(spd);
00622 
00623   spd += v->progress;
00624   v->progress = (byte)spd;
00625   return spd >> 8;
00626 }
00627 
00635 int GetAircraftFlyingAltitude(const Aircraft *v)
00636 {
00637   if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00638 
00639   /* Make sure Aircraft fly no lower so that they don't conduct
00640    * CFITs (controlled flight into terrain)
00641    */
00642   int base_altitude = PLANE_HOLDING_ALTITUDE;
00643 
00644   /* Make sure eastbound and westbound planes do not "crash" into each
00645    * other by providing them with vertical seperation
00646    */
00647   switch (v->direction) {
00648     case DIR_N:
00649     case DIR_NE:
00650     case DIR_E:
00651     case DIR_SE:
00652       base_altitude += 10;
00653       break;
00654 
00655     default: break;
00656   }
00657 
00658   /* Make faster planes fly higher so that they can overtake slower ones */
00659   base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00660 
00661   return base_altitude;
00662 }
00663 
00678 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00679 {
00680   assert(v != NULL);
00681   assert(apc != NULL);
00682 
00683   /* In the case the station doesn't exit anymore, set target tile 0.
00684    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00685    * or it will simply crash in next tick */
00686   TileIndex tile = 0;
00687 
00688   const Station *st = Station::GetIfValid(v->targetairport);
00689   if (st != NULL) {
00690     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00691     tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00692   }
00693 
00694   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00695   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00696 
00697   DiagDirection dir;
00698   if (abs(delta_y) < abs(delta_x)) {
00699     /* We are northeast or southwest of the airport */
00700     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00701   } else {
00702     /* We are northwest or southeast of the airport */
00703     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00704   }
00705   dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00706   return apc->entry_points[dir];
00707 }
00708 
00709 
00710 static void MaybeCrashAirplane(Aircraft *v);
00711 
00719 static bool AircraftController(Aircraft *v)
00720 {
00721   int count;
00722 
00723   /* NULL if station is invalid */
00724   const Station *st = Station::GetIfValid(v->targetairport);
00725   /* INVALID_TILE if there is no station */
00726   TileIndex tile = INVALID_TILE;
00727   Direction rotation = DIR_N;
00728   uint size_x = 1, size_y = 1;
00729   if (st != NULL) {
00730     if (st->airport.tile != INVALID_TILE) {
00731       tile = st->airport.tile;
00732       rotation = st->airport.rotation;
00733       size_x = st->airport.w;
00734       size_y = st->airport.h;
00735     } else {
00736       tile = st->xy;
00737     }
00738   }
00739   /* DUMMY if there is no station or no airport */
00740   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00741 
00742   /* prevent going to INVALID_TILE if airport is deleted. */
00743   if (st == NULL || st->airport.tile == INVALID_TILE) {
00744     /* Jump into our "holding pattern" state machine if possible */
00745     if (v->pos >= afc->nofelements) {
00746       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00747     } else if (v->targetairport != v->current_order.GetDestination()) {
00748       /* If not possible, just get out of here fast */
00749       v->state = FLYING;
00750       UpdateAircraftCache(v);
00751       AircraftNextAirportPos_and_Order(v);
00752       /* get aircraft back on running altitude */
00753       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00754       return false;
00755     }
00756   }
00757 
00758   /*  get airport moving data */
00759   const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00760 
00761   int x = TileX(tile) * TILE_SIZE;
00762   int y = TileY(tile) * TILE_SIZE;
00763 
00764   /* Helicopter raise */
00765   if (amd.flag & AMED_HELI_RAISE) {
00766     Aircraft *u = v->Next()->Next();
00767 
00768     /* Make sure the rotors don't rotate too fast */
00769     if (u->cur_speed > 32) {
00770       v->cur_speed = 0;
00771       if (--u->cur_speed == 32) {
00772         if (!PlayVehicleSound(v, VSE_START)) {
00773           SndPlayVehicleFx(SND_18_HELICOPTER, v);
00774         }
00775       }
00776     } else {
00777       u->cur_speed = 32;
00778       count = UpdateAircraftSpeed(v);
00779       if (count > 0) {
00780         v->tile = 0;
00781         int z_dest = GetAircraftFlyingAltitude(v);
00782 
00783         /* Reached altitude? */
00784         if (v->z_pos >= z_dest) {
00785           v->cur_speed = 0;
00786           return true;
00787         }
00788         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00789       }
00790     }
00791     return false;
00792   }
00793 
00794   /* Helicopter landing. */
00795   if (amd.flag & AMED_HELI_LOWER) {
00796     if (st == NULL) {
00797       /* FIXME - AircraftController -> if station no longer exists, do not land
00798        * helicopter will circle until sign disappears, then go to next order
00799        * what to do when it is the only order left, right now it just stays in 1 place */
00800       v->state = FLYING;
00801       UpdateAircraftCache(v);
00802       AircraftNextAirportPos_and_Order(v);
00803       return false;
00804     }
00805 
00806     /* Vehicle is now at the airport. */
00807     v->tile = tile;
00808 
00809     /* Find altitude of landing position. */
00810     int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00811 
00812     if (z == v->z_pos) {
00813       Vehicle *u = v->Next()->Next();
00814 
00815       /*  Increase speed of rotors. When speed is 80, we've landed. */
00816       if (u->cur_speed >= 80) return true;
00817       u->cur_speed += 4;
00818     } else {
00819       count = UpdateAircraftSpeed(v);
00820       if (count > 0) {
00821         if (v->z_pos > z) {
00822           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00823         } else {
00824           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00825         }
00826       }
00827     }
00828     return false;
00829   }
00830 
00831   /* Get distance from destination pos to current pos. */
00832   uint dist = abs(x + amd.x - v->x_pos) +  abs(y + amd.y - v->y_pos);
00833 
00834   /* Need exact position? */
00835   if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00836 
00837   /* At final pos? */
00838   if (dist == 0) {
00839     /* Change direction smoothly to final direction. */
00840     DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00841     /* if distance is 0, and plane points in right direction, no point in calling
00842      * UpdateAircraftSpeed(). So do it only afterwards */
00843     if (dirdiff == DIRDIFF_SAME) {
00844       v->cur_speed = 0;
00845       return true;
00846     }
00847 
00848     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00849 
00850     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00851     v->cur_speed >>= 1;
00852 
00853     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00854     return false;
00855   }
00856 
00857   if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00858     MaybeCrashAirplane(v);
00859     if ((v->vehstatus & VS_CRASHED) != 0) return false;
00860   }
00861 
00862   uint speed_limit = SPEED_LIMIT_TAXI;
00863   bool hard_limit = true;
00864 
00865   if (amd.flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
00866   if (amd.flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
00867   if (amd.flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00868   if (amd.flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
00869 
00870   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00871   if (count == 0) return false;
00872 
00873   if (v->turn_counter != 0) v->turn_counter--;
00874 
00875   do {
00876 
00877     GetNewVehiclePosResult gp;
00878 
00879     if (dist < 4 || (amd.flag & AMED_LAND)) {
00880       /* move vehicle one pixel towards target */
00881       gp.x = (v->x_pos != (x + amd.x)) ?
00882           v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00883           v->x_pos;
00884       gp.y = (v->y_pos != (y + amd.y)) ?
00885           v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00886           v->y_pos;
00887 
00888       /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
00889       gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00890 
00891     } else {
00892 
00893       /* Turn. Do it slowly if in the air. */
00894       Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00895       if (newdir != v->direction) {
00896         if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00897           if (v->turn_counter == 0 || newdir == v->last_direction) {
00898             if (newdir == v->last_direction) {
00899               v->number_consecutive_turns = 0;
00900             } else {
00901               v->number_consecutive_turns++;
00902             }
00903             v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00904             v->last_direction = v->direction;
00905             v->direction = newdir;
00906           }
00907 
00908           /* Move vehicle. */
00909           gp = GetNewVehiclePos(v);
00910         } else {
00911           v->cur_speed >>= 1;
00912           v->direction = newdir;
00913 
00914           /* When leaving a terminal an aircraft often goes to a position
00915            * directly in front of it. If it would move while turning it
00916            * would need an two extra turns to end up at the correct position.
00917            * To make it easier just disallow all moving while turning as
00918            * long as an aircraft is on the ground. */
00919           gp.x = v->x_pos;
00920           gp.y = v->y_pos;
00921           gp.new_tile = gp.old_tile = v->tile;
00922         }
00923       } else {
00924         v->number_consecutive_turns = 0;
00925         /* Move vehicle. */
00926         gp = GetNewVehiclePos(v);
00927       }
00928     }
00929 
00930     v->tile = gp.new_tile;
00931     /* If vehicle is in the air, use tile coordinate 0. */
00932     if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00933 
00934     /* Adjust Z for land or takeoff? */
00935     int z = v->z_pos;
00936 
00937     if (amd.flag & AMED_TAKEOFF) {
00938       z = min(z + 2, GetAircraftFlyingAltitude(v));
00939     }
00940 
00941     /* Let the plane drop from normal flight altitude to holding pattern altitude */
00942     if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00943 
00944     if (amd.flag & AMED_LAND) {
00945       if (st->airport.tile == INVALID_TILE) {
00946         /* Airport has been removed, abort the landing procedure */
00947         v->state = FLYING;
00948         UpdateAircraftCache(v);
00949         AircraftNextAirportPos_and_Order(v);
00950         /* get aircraft back on running altitude */
00951         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00952         continue;
00953       }
00954 
00955       int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00956 
00957       /* We're not flying below our destination, right? */
00958       assert(curz <= z);
00959       int t = max(1U, dist - 4);
00960       int delta = z - curz;
00961 
00962       /* Only start lowering when we're sufficiently close for a 1:1 glide */
00963       if (delta >= t) {
00964         z -= CeilDiv(z - curz, t);
00965       }
00966       if (z < curz) z = curz;
00967     }
00968 
00969     /* We've landed. Decrease speed when we're reaching end of runway. */
00970     if (amd.flag & AMED_BRAKE) {
00971       int curz = GetSlopePixelZ(x, y) + 1;
00972 
00973       if (z > curz) {
00974         z--;
00975       } else if (z < curz) {
00976         z++;
00977       }
00978 
00979     }
00980 
00981     SetAircraftPosition(v, gp.x, gp.y, z);
00982   } while (--count != 0);
00983   return false;
00984 }
00985 
00990 static bool HandleCrashedAircraft(Aircraft *v)
00991 {
00992   v->crashed_counter += 3;
00993 
00994   Station *st = GetTargetAirportIfValid(v);
00995 
00996   /* make aircraft crash down to the ground */
00997   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
00998     int z = GetSlopePixelZ(v->x_pos, v->y_pos);
00999     v->z_pos -= 1;
01000     if (v->z_pos == z) {
01001       v->crashed_counter = 500;
01002       v->z_pos++;
01003     }
01004   }
01005 
01006   if (v->crashed_counter < 650) {
01007     uint32 r;
01008     if (Chance16R(1, 32, r)) {
01009       static const DirDiff delta[] = {
01010         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01011       };
01012 
01013       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01014       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01015       r = Random();
01016       CreateEffectVehicleRel(v,
01017         GB(r, 0, 4) - 4,
01018         GB(r, 4, 4) - 4,
01019         GB(r, 8, 4),
01020         EV_EXPLOSION_SMALL);
01021     }
01022   } else if (v->crashed_counter >= 10000) {
01023     /*  remove rubble of crashed airplane */
01024 
01025     /* clear runway-in on all airports, set by crashing plane
01026      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01027      * but they all share the same number */
01028     if (st != NULL) {
01029       CLRBITS(st->airport.flags, RUNWAY_IN_block);
01030       CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
01031       CLRBITS(st->airport.flags, RUNWAY_IN2_block);    // intercontinental
01032     }
01033 
01034     delete v;
01035 
01036     return false;
01037   }
01038 
01039   return true;
01040 }
01041 
01042 
01043 static void HandleAircraftSmoke(Aircraft *v)
01044 {
01045   static const struct {
01046     int8 x;
01047     int8 y;
01048   } smoke_pos[] = {
01049     {  5,  5 },
01050     {  6,  0 },
01051     {  5, -5 },
01052     {  0, -6 },
01053     { -5, -5 },
01054     { -6,  0 },
01055     { -5,  5 },
01056     {  0,  6 }
01057   };
01058 
01059   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01060 
01061   if (v->cur_speed < 10) {
01062     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01063     v->breakdown_ctr = 0;
01064     return;
01065   }
01066 
01067   if ((v->tick_counter & 0x1F) == 0) {
01068     CreateEffectVehicleRel(v,
01069       smoke_pos[v->direction].x,
01070       smoke_pos[v->direction].y,
01071       2,
01072       EV_BREAKDOWN_SMOKE_AIRCRAFT
01073     );
01074   }
01075 }
01076 
01077 void HandleMissingAircraftOrders(Aircraft *v)
01078 {
01079   /*
01080    * We do not have an order. This can be divided into two cases:
01081    * 1) we are heading to an invalid station. In this case we must
01082    *    find another airport to go to. If there is nowhere to go,
01083    *    we will destroy the aircraft as it otherwise will enter
01084    *    the holding pattern for the first airport, which can cause
01085    *    the plane to go into an undefined state when building an
01086    *    airport with the same StationID.
01087    * 2) we are (still) heading to a (still) valid airport, then we
01088    *    can continue going there. This can happen when you are
01089    *    changing the aircraft's orders while in-flight or in for
01090    *    example a depot. However, when we have a current order to
01091    *    go to a depot, we have to keep that order so the aircraft
01092    *    actually stops.
01093    */
01094   const Station *st = GetTargetAirportIfValid(v);
01095   if (st == NULL) {
01096     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01097     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01098     cur_company.Restore();
01099 
01100     if (ret.Failed()) CrashAirplane(v);
01101   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01102     v->current_order.Free();
01103   }
01104 }
01105 
01106 
01107 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01108 {
01109   /* Orders are changed in flight, ensure going to the right station. */
01110   if (this->state == FLYING) {
01111     AircraftNextAirportPos_and_Order(this);
01112   }
01113 
01114   /* Aircraft do not use dest-tile */
01115   return 0;
01116 }
01117 
01118 void Aircraft::MarkDirty()
01119 {
01120   this->UpdateViewport(false, false);
01121   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01122 }
01123 
01124 
01125 uint Aircraft::Crash(bool flooded)
01126 {
01127   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01128   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01129 
01130   return pass;
01131 }
01132 
01137 static void CrashAirplane(Aircraft *v)
01138 {
01139   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01140 
01141   uint pass = v->Crash();
01142   SetDParam(0, pass);
01143 
01144   v->cargo.Truncate(0);
01145   v->Next()->cargo.Truncate(0);
01146   const Station *st = GetTargetAirportIfValid(v);
01147   StringID newsitem;
01148   if (st == NULL) {
01149     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01150   } else {
01151     SetDParam(1, st->index);
01152     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01153   }
01154 
01155   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01156 
01157   AddVehicleNewsItem(newsitem,
01158     NS_ACCIDENT,
01159     v->index,
01160     st != NULL ? st->index : INVALID_STATION);
01161 
01162   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01163   SndPlayVehicleFx(SND_12_EXPLOSION, v);
01164 }
01165 
01170 static void MaybeCrashAirplane(Aircraft *v)
01171 {
01172   if (_settings_game.vehicle.plane_crashes == 0) return;
01173 
01174   Station *st = Station::Get(v->targetairport);
01175 
01176   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01177   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01178   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01179       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01180       !_cheats.no_jetcrash.value) {
01181     prob /= 20;
01182   } else {
01183     prob /= 1500;
01184   }
01185 
01186   if (GB(Random(), 0, 22) > prob) return;
01187 
01188   /* Crash the airplane. Remove all goods stored at the station. */
01189   for (CargoID i = 0; i < NUM_CARGO; i++) {
01190     st->goods[i].rating = 1;
01191     st->goods[i].cargo.Truncate(0);
01192   }
01193 
01194   CrashAirplane(v);
01195 }
01196 
01202 static void AircraftEntersTerminal(Aircraft *v)
01203 {
01204   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01205 
01206   Station *st = Station::Get(v->targetairport);
01207   v->last_station_visited = v->targetairport;
01208 
01209   /* Check if station was ever visited before */
01210   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01211     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01212     SetDParam(0, st->index);
01213     /* show newsitem of celebrating citizens */
01214     AddVehicleNewsItem(
01215       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01216       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01217       v->index,
01218       st->index
01219     );
01220     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01221   }
01222 
01223   v->BeginLoading();
01224 }
01225 
01230 static void AircraftLandAirplane(Aircraft *v)
01231 {
01232   v->UpdateDeltaXY(INVALID_DIR);
01233 
01234   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01235     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01236   }
01237 }
01238 
01239 
01241 void AircraftNextAirportPos_and_Order(Aircraft *v)
01242 {
01243   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01244     v->targetairport = v->current_order.GetDestination();
01245   }
01246 
01247   const Station *st = GetTargetAirportIfValid(v);
01248   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01249   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01250   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01251 }
01252 
01261 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01262 {
01263   v->cur_speed = 0;
01264   v->subspeed = 0;
01265   v->progress = 0;
01266   v->direction = exit_dir;
01267   v->vehstatus &= ~VS_HIDDEN;
01268   {
01269     Vehicle *u = v->Next();
01270     u->vehstatus &= ~VS_HIDDEN;
01271 
01272     /* Rotor blades */
01273     u = u->Next();
01274     if (u != NULL) {
01275       u->vehstatus &= ~VS_HIDDEN;
01276       u->cur_speed = 80;
01277     }
01278   }
01279 
01280   VehicleServiceInDepot(v);
01281   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01282   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01283   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01284 }
01285 
01289 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01290 {
01291   AircraftEntersTerminal(v);
01292   v->state = apc->layout[v->pos].heading;
01293 }
01294 
01300 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01301 {
01302   VehicleEnterDepot(v);
01303   v->state = apc->layout[v->pos].heading;
01304 }
01305 
01311 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01312 {
01313   /* if we just arrived, execute EnterHangar first */
01314   if (v->previous_pos != v->pos) {
01315     AircraftEventHandler_EnterHangar(v, apc);
01316     return;
01317   }
01318 
01319   /* if we were sent to the depot, stay there */
01320   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01321     v->current_order.Free();
01322     return;
01323   }
01324 
01325   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01326       !v->current_order.IsType(OT_GOTO_DEPOT))
01327     return;
01328 
01329   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
01330   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01331     VehicleEnterDepot(v);
01332     return;
01333   }
01334 
01335   /* if the block of the next position is busy, stay put */
01336   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01337 
01338   /* We are already at the target airport, we need to find a terminal */
01339   if (v->current_order.GetDestination() == v->targetairport) {
01340     /* FindFreeTerminal:
01341      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01342     if (v->subtype == AIR_HELICOPTER) {
01343       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01344     } else {
01345       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01346     }
01347   } else { // Else prepare for launch.
01348     /* airplane goto state takeoff, helicopter to helitakeoff */
01349     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01350   }
01351   const Station *st = Station::GetByTile(v->tile);
01352   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01353   AirportMove(v, apc);
01354 }
01355 
01357 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01358 {
01359   /* if we just arrived, execute EnterTerminal first */
01360   if (v->previous_pos != v->pos) {
01361     AircraftEventHandler_EnterTerminal(v, apc);
01362     /* on an airport with helipads, a helicopter will always land there
01363      * and get serviced at the same time - setting */
01364     if (_settings_game.order.serviceathelipad) {
01365       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01366         /* an exerpt of ServiceAircraft, without the invisibility stuff */
01367         v->date_of_last_service = _date;
01368         v->breakdowns_since_last_service = 0;
01369         v->reliability = v->GetEngine()->reliability;
01370         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01371       }
01372     }
01373     return;
01374   }
01375 
01376   if (v->current_order.IsType(OT_NOTHING)) return;
01377 
01378   /* if the block of the next position is busy, stay put */
01379   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01380 
01381   /* airport-road is free. We either have to go to another airport, or to the hangar
01382    * ---> start moving */
01383 
01384   bool go_to_hangar = false;
01385   switch (v->current_order.GetType()) {
01386     case OT_GOTO_STATION: // ready to fly to another airport
01387       break;
01388     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
01389       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01390       break;
01391     case OT_CONDITIONAL:
01392       /* In case of a conditional order we just have to wait a tick
01393        * longer, so the conditional order can actually be processed;
01394        * we should not clear the order as that makes us go nowhere. */
01395       return;
01396     default:  // orders have been deleted (no orders), goto depot and don't bother us
01397       v->current_order.Free();
01398       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01399   }
01400 
01401   if (go_to_hangar) {
01402     v->state = HANGAR;
01403   } else {
01404     /* airplane goto state takeoff, helicopter to helitakeoff */
01405     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01406   }
01407   AirportMove(v, apc);
01408 }
01409 
01410 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01411 {
01412   error("OK, you shouldn't be here, check your Airport Scheme!");
01413 }
01414 
01415 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01416 {
01417   PlayAircraftSound(v); // play takeoffsound for airplanes
01418   v->state = STARTTAKEOFF;
01419 }
01420 
01421 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01422 {
01423   v->state = ENDTAKEOFF;
01424   v->UpdateDeltaXY(INVALID_DIR);
01425 }
01426 
01427 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01428 {
01429   v->state = FLYING;
01430   /* get the next position to go to, differs per airport */
01431   AircraftNextAirportPos_and_Order(v);
01432 }
01433 
01434 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01435 {
01436   v->state = FLYING;
01437   v->UpdateDeltaXY(INVALID_DIR);
01438 
01439   /* get the next position to go to, differs per airport */
01440   AircraftNextAirportPos_and_Order(v);
01441 
01442   /* Send the helicopter to a hangar if needed for replacement */
01443   if (v->NeedsAutomaticServicing()) {
01444     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01445     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01446     cur_company.Restore();
01447   }
01448 }
01449 
01450 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01451 {
01452   Station *st = Station::Get(v->targetairport);
01453 
01454   /* runway busy or not allowed to use this airstation, circle */
01455   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01456     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01457      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01458      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01459     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01460     const AirportFTA *current = apc->layout[v->pos].next;
01461     while (current != NULL) {
01462       if (current->heading == landingtype) {
01463         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01464          * we don't want that for plane in air
01465          * hack for speed thingie */
01466         uint16 tcur_speed = v->cur_speed;
01467         uint16 tsubspeed = v->subspeed;
01468         if (!AirportHasBlock(v, current, apc)) {
01469           v->state = landingtype; // LANDING / HELILANDING
01470           /* it's a bit dirty, but I need to set position to next position, otherwise
01471            * if there are multiple runways, plane won't know which one it took (because
01472            * they all have heading LANDING). And also occupy that block! */
01473           v->pos = current->next_position;
01474           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01475           return;
01476         }
01477         v->cur_speed = tcur_speed;
01478         v->subspeed = tsubspeed;
01479       }
01480       current = current->next;
01481     }
01482   }
01483   v->state = FLYING;
01484   v->pos = apc->layout[v->pos].next_position;
01485 }
01486 
01487 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01488 {
01489   v->state = ENDLANDING;
01490   AircraftLandAirplane(v);  // maybe crash airplane
01491 
01492   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01493   if (v->NeedsAutomaticServicing()) {
01494     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01495     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01496     cur_company.Restore();
01497   }
01498 }
01499 
01500 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01501 {
01502   v->state = HELIENDLANDING;
01503   v->UpdateDeltaXY(INVALID_DIR);
01504 }
01505 
01506 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01507 {
01508   /* next block busy, don't do a thing, just wait */
01509   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01510 
01511   /* if going to terminal (OT_GOTO_STATION) choose one
01512    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01513    * 2. not going for terminal (but depot, no order),
01514    * --> get out of the way to the hangar. */
01515   if (v->current_order.IsType(OT_GOTO_STATION)) {
01516     if (AirportFindFreeTerminal(v, apc)) return;
01517   }
01518   v->state = HANGAR;
01519 
01520 }
01521 
01522 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01523 {
01524   /*  next block busy, don't do a thing, just wait */
01525   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01526 
01527   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01528    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01529    * 2. not going for terminal (but depot, no order),
01530    * --> get out of the way to the hangar IF there are terminals on the airport.
01531    * --> else TAKEOFF
01532    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01533    * must go to a hangar. */
01534   if (v->current_order.IsType(OT_GOTO_STATION)) {
01535     if (AirportFindFreeHelipad(v, apc)) return;
01536   }
01537   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01538 }
01539 
01545 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01547 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01548   AircraftEventHandler_General,        // TO_ALL         =  0
01549   AircraftEventHandler_InHangar,       // HANGAR         =  1
01550   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01551   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01552   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01553   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01554   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01555   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01556   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01557   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01558   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01559   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01560   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01561   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01562   AircraftEventHandler_Flying,         // FLYING         = 14
01563   AircraftEventHandler_Landing,        // LANDING        = 15
01564   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01565   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01566   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01567   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01568   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01569   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01570 };
01571 
01572 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01573 {
01574   /* we have left the previous block, and entered the new one. Free the previous block */
01575   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01576     Station *st = Station::Get(v->targetairport);
01577 
01578     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01579   }
01580 }
01581 
01582 static void AirportGoToNextPosition(Aircraft *v)
01583 {
01584   /* if aircraft is not in position, wait until it is */
01585   if (!AircraftController(v)) return;
01586 
01587   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01588 
01589   AirportClearBlock(v, apc);
01590   AirportMove(v, apc); // move aircraft to next position
01591 }
01592 
01593 /* gets pos from vehicle and next orders */
01594 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01595 {
01596   /* error handling */
01597   if (v->pos >= apc->nofelements) {
01598     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01599     assert(v->pos < apc->nofelements);
01600   }
01601 
01602   const AirportFTA *current = &apc->layout[v->pos];
01603   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01604   if (current->heading == v->state) {
01605     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01606     byte prev_state = v->state;
01607     _aircraft_state_handlers[v->state](v, apc);
01608     if (v->state != FLYING) v->previous_pos = prev_pos;
01609     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01610     return true;
01611   }
01612 
01613   v->previous_pos = v->pos; // save previous location
01614 
01615   /* there is only one choice to move to */
01616   if (current->next == NULL) {
01617     if (AirportSetBlocks(v, current, apc)) {
01618       v->pos = current->next_position;
01619       UpdateAircraftCache(v);
01620     } // move to next position
01621     return false;
01622   }
01623 
01624   /* there are more choices to choose from, choose the one that
01625    * matches our heading */
01626   do {
01627     if (v->state == current->heading || current->heading == TO_ALL) {
01628       if (AirportSetBlocks(v, current, apc)) {
01629         v->pos = current->next_position;
01630         UpdateAircraftCache(v);
01631       } // move to next position
01632       return false;
01633     }
01634     current = current->next;
01635   } while (current != NULL);
01636 
01637   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01638   NOT_REACHED();
01639 }
01640 
01642 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01643 {
01644   const AirportFTA *reference = &apc->layout[v->pos];
01645   const AirportFTA *next = &apc->layout[current_pos->next_position];
01646 
01647   /* same block, then of course we can move */
01648   if (apc->layout[current_pos->position].block != next->block) {
01649     const Station *st = Station::Get(v->targetairport);
01650     uint64 airport_flags = next->block;
01651 
01652     /* check additional possible extra blocks */
01653     if (current_pos != reference && current_pos->block != NOTHING_block) {
01654       airport_flags |= current_pos->block;
01655     }
01656 
01657     if (st->airport.flags & airport_flags) {
01658       v->cur_speed = 0;
01659       v->subspeed = 0;
01660       return true;
01661     }
01662   }
01663   return false;
01664 }
01665 
01673 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01674 {
01675   const AirportFTA *next = &apc->layout[current_pos->next_position];
01676   const AirportFTA *reference = &apc->layout[v->pos];
01677 
01678   /* if the next position is in another block, check it and wait until it is free */
01679   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01680     uint64 airport_flags = next->block;
01681     /* search for all all elements in the list with the same state, and blocks != N
01682      * this means more blocks should be checked/set */
01683     const AirportFTA *current = current_pos;
01684     if (current == reference) current = current->next;
01685     while (current != NULL) {
01686       if (current->heading == current_pos->heading && current->block != 0) {
01687         airport_flags |= current->block;
01688         break;
01689       }
01690       current = current->next;
01691     }
01692 
01693     /* if the block to be checked is in the next position, then exclude that from
01694      * checking, because it has been set by the airplane before */
01695     if (current_pos->block == next->block) airport_flags ^= next->block;
01696 
01697     Station *st = Station::Get(v->targetairport);
01698     if (st->airport.flags & airport_flags) {
01699       v->cur_speed = 0;
01700       v->subspeed = 0;
01701       return false;
01702     }
01703 
01704     if (next->block != NOTHING_block) {
01705       SETBITS(st->airport.flags, airport_flags); // occupy next block
01706     }
01707   }
01708   return true;
01709 }
01710 
01715 struct MovementTerminalMapping {
01716   AirportMovementStates state; 
01717   uint64 airport_flag;         
01718 };
01719 
01721 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01722   {TERM1, TERM1_block},
01723   {TERM2, TERM2_block},
01724   {TERM3, TERM3_block},
01725   {TERM4, TERM4_block},
01726   {TERM5, TERM5_block},
01727   {TERM6, TERM6_block},
01728   {TERM7, TERM7_block},
01729   {TERM8, TERM8_block},
01730   {HELIPAD1, HELIPAD1_block},
01731   {HELIPAD2, HELIPAD2_block},
01732   {HELIPAD3, HELIPAD3_block},
01733 };
01734 
01742 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01743 {
01744   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01745   Station *st = Station::Get(v->targetairport);
01746   for (; i < last_terminal; i++) {
01747     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01748       /* TERMINAL# HELIPAD# */
01749       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01750       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01751       return true;
01752     }
01753   }
01754   return false;
01755 }
01756 
01762 static uint GetNumTerminals(const AirportFTAClass *apc)
01763 {
01764   uint num = 0;
01765 
01766   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01767 
01768   return num;
01769 }
01770 
01777 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01778 {
01779   /* example of more terminalgroups
01780    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01781    * Heading 255 denotes a group. We see 2 groups here:
01782    * 1. group 0 -- TERM_GROUP1_block (check block)
01783    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01784    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01785    * looks at the corresponding terminals of that group. If no free ones are found, other
01786    * possible groups are checked (in this case group 1, since that is after group 0). If that
01787    * fails, then attempt fails and plane waits
01788    */
01789   if (apc->terminals[0] > 1) {
01790     const Station *st = Station::Get(v->targetairport);
01791     const AirportFTA *temp = apc->layout[v->pos].next;
01792 
01793     while (temp != NULL) {
01794       if (temp->heading == 255) {
01795         if (!(st->airport.flags & temp->block)) {
01796           /* read which group do we want to go to?
01797            * (the first free group) */
01798           uint target_group = temp->next_position + 1;
01799 
01800           /* at what terminal does the group start?
01801            * that means, sum up all terminals of
01802            * groups with lower number */
01803           uint group_start = 0;
01804           for (uint i = 1; i < target_group; i++) {
01805             group_start += apc->terminals[i];
01806           }
01807 
01808           uint group_end = group_start + apc->terminals[target_group];
01809           if (FreeTerminal(v, group_start, group_end)) return true;
01810         }
01811       } else {
01812         /* once the heading isn't 255, we've exhausted the possible blocks.
01813          * So we cannot move */
01814         return false;
01815       }
01816       temp = temp->next;
01817     }
01818   }
01819 
01820   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01821   return FreeTerminal(v, 0, GetNumTerminals(apc));
01822 }
01823 
01830 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01831 {
01832   /* if an airport doesn't have helipads, use terminals */
01833   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01834 
01835   /* only 1 helicoptergroup, check all helipads
01836    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01837   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01838 }
01839 
01840 static bool AircraftEventHandler(Aircraft *v, int loop)
01841 {
01842   v->tick_counter++;
01843 
01844   if (v->vehstatus & VS_CRASHED) {
01845     return HandleCrashedAircraft(v);
01846   }
01847 
01848   if (v->vehstatus & VS_STOPPED) return true;
01849 
01850   v->HandleBreakdown();
01851 
01852   HandleAircraftSmoke(v);
01853   ProcessOrders(v);
01854   v->HandleLoading(loop != 0);
01855 
01856   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01857 
01858   AirportGoToNextPosition(v);
01859 
01860   return true;
01861 }
01862 
01863 bool Aircraft::Tick()
01864 {
01865   if (!this->IsNormalAircraft()) return true;
01866 
01867   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01868 
01869   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01870 
01871   this->current_order_time++;
01872 
01873   for (uint i = 0; i != 2; i++) {
01874     /* stop if the aircraft was deleted */
01875     if (!AircraftEventHandler(this, i)) return false;
01876   }
01877 
01878   return true;
01879 }
01880 
01881 
01888 Station *GetTargetAirportIfValid(const Aircraft *v)
01889 {
01890   assert(v->type == VEH_AIRCRAFT);
01891 
01892   Station *st = Station::GetIfValid(v->targetairport);
01893   if (st == NULL) return NULL;
01894 
01895   return st->airport.tile == INVALID_TILE ? NULL : st;
01896 }
01897 
01902 void UpdateAirplanesOnNewStation(const Station *st)
01903 {
01904   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01905   const AirportFTAClass *ap = st->airport.GetFTA();
01906   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01907 
01908   Aircraft *v;
01909   FOR_ALL_AIRCRAFT(v) {
01910     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01911     assert(v->state == FLYING);
01912     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01913     UpdateAircraftCache(v);
01914   }
01915 }