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, true);
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 
00543 void UpdateAircraftCache(Aircraft *v, bool update_range)
00544 {
00545   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00546   if (max_speed != 0) {
00547     /* Convert from original units to km-ish/h */
00548     max_speed = (max_speed * 128) / 10;
00549 
00550     v->vcache.cached_max_speed = max_speed;
00551   } else {
00552     /* Use the default max speed of the vehicle. */
00553     v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00554   }
00555 
00556   /* Update cargo aging period. */
00557   v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00558   Aircraft *u = v->Next(); // Shadow for mail
00559   u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00560 
00561   /* Update aircraft range. */
00562   if (update_range) {
00563     v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
00564     /* Squared it now so we don't have to do it later all the time. */
00565     v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
00566   }
00567 }
00568 
00569 
00573 enum AircraftSpeedLimits {
00574   SPEED_LIMIT_TAXI     =     50,  
00575   SPEED_LIMIT_APPROACH =    230,  
00576   SPEED_LIMIT_BROKEN   =    320,  
00577   SPEED_LIMIT_HOLD     =    425,  
00578   SPEED_LIMIT_NONE     = 0xFFFF   
00579 };
00580 
00588 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00589 {
00590   uint spd = v->acceleration * 16;
00591   byte t;
00592 
00593   /* Adjust speed limits by plane speed factor to prevent taxiing
00594    * and take-off speeds being too low. */
00595   speed_limit *= _settings_game.vehicle.plane_speed;
00596 
00597   if (v->vcache.cached_max_speed < speed_limit) {
00598     if (v->cur_speed < speed_limit) hard_limit = false;
00599     speed_limit = v->vcache.cached_max_speed;
00600   }
00601 
00602   v->subspeed = (t = v->subspeed) + (byte)spd;
00603 
00604   /* Aircraft's current speed is used twice so that very fast planes are
00605    * forced to slow down rapidly in the short distance needed. The magic
00606    * value 16384 was determined to give similar results to the old speed/48
00607    * method at slower speeds. This also results in less reduction at slow
00608    * speeds to that aircraft do not get to taxi speed straight after
00609    * touchdown. */
00610   if (!hard_limit && v->cur_speed > speed_limit) {
00611     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00612   }
00613 
00614   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00615 
00616   /* adjust speed for broken vehicles */
00617   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00618 
00619   /* updates statusbar only if speed have changed to save CPU time */
00620   if (spd != v->cur_speed) {
00621     v->cur_speed = spd;
00622     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00623   }
00624 
00625   /* Adjust distance moved by plane speed setting */
00626   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00627 
00628   /* Convert direction-indepenent speed into direction-dependent speed. (old movement method) */
00629   spd = v->GetOldAdvanceSpeed(spd);
00630 
00631   spd += v->progress;
00632   v->progress = (byte)spd;
00633   return spd >> 8;
00634 }
00635 
00643 int GetAircraftFlyingAltitude(const Aircraft *v)
00644 {
00645   if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00646 
00647   /* Make sure Aircraft fly no lower so that they don't conduct
00648    * CFITs (controlled flight into terrain)
00649    */
00650   int base_altitude = PLANE_HOLDING_ALTITUDE;
00651 
00652   /* Make sure eastbound and westbound planes do not "crash" into each
00653    * other by providing them with vertical seperation
00654    */
00655   switch (v->direction) {
00656     case DIR_N:
00657     case DIR_NE:
00658     case DIR_E:
00659     case DIR_SE:
00660       base_altitude += 10;
00661       break;
00662 
00663     default: break;
00664   }
00665 
00666   /* Make faster planes fly higher so that they can overtake slower ones */
00667   base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00668 
00669   return base_altitude;
00670 }
00671 
00686 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00687 {
00688   assert(v != NULL);
00689   assert(apc != NULL);
00690 
00691   /* In the case the station doesn't exit anymore, set target tile 0.
00692    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00693    * or it will simply crash in next tick */
00694   TileIndex tile = 0;
00695 
00696   const Station *st = Station::GetIfValid(v->targetairport);
00697   if (st != NULL) {
00698     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00699     tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00700   }
00701 
00702   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00703   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00704 
00705   DiagDirection dir;
00706   if (abs(delta_y) < abs(delta_x)) {
00707     /* We are northeast or southwest of the airport */
00708     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00709   } else {
00710     /* We are northwest or southeast of the airport */
00711     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00712   }
00713   dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00714   return apc->entry_points[dir];
00715 }
00716 
00717 
00718 static void MaybeCrashAirplane(Aircraft *v);
00719 
00727 static bool AircraftController(Aircraft *v)
00728 {
00729   int count;
00730 
00731   /* NULL if station is invalid */
00732   const Station *st = Station::GetIfValid(v->targetairport);
00733   /* INVALID_TILE if there is no station */
00734   TileIndex tile = INVALID_TILE;
00735   Direction rotation = DIR_N;
00736   uint size_x = 1, size_y = 1;
00737   if (st != NULL) {
00738     if (st->airport.tile != INVALID_TILE) {
00739       tile = st->airport.tile;
00740       rotation = st->airport.rotation;
00741       size_x = st->airport.w;
00742       size_y = st->airport.h;
00743     } else {
00744       tile = st->xy;
00745     }
00746   }
00747   /* DUMMY if there is no station or no airport */
00748   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00749 
00750   /* prevent going to INVALID_TILE if airport is deleted. */
00751   if (st == NULL || st->airport.tile == INVALID_TILE) {
00752     /* Jump into our "holding pattern" state machine if possible */
00753     if (v->pos >= afc->nofelements) {
00754       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00755     } else if (v->targetairport != v->current_order.GetDestination()) {
00756       /* If not possible, just get out of here fast */
00757       v->state = FLYING;
00758       UpdateAircraftCache(v);
00759       AircraftNextAirportPos_and_Order(v);
00760       /* get aircraft back on running altitude */
00761       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00762       return false;
00763     }
00764   }
00765 
00766   /*  get airport moving data */
00767   const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00768 
00769   int x = TileX(tile) * TILE_SIZE;
00770   int y = TileY(tile) * TILE_SIZE;
00771 
00772   /* Helicopter raise */
00773   if (amd.flag & AMED_HELI_RAISE) {
00774     Aircraft *u = v->Next()->Next();
00775 
00776     /* Make sure the rotors don't rotate too fast */
00777     if (u->cur_speed > 32) {
00778       v->cur_speed = 0;
00779       if (--u->cur_speed == 32) {
00780         if (!PlayVehicleSound(v, VSE_START)) {
00781           SndPlayVehicleFx(SND_18_HELICOPTER, v);
00782         }
00783       }
00784     } else {
00785       u->cur_speed = 32;
00786       count = UpdateAircraftSpeed(v);
00787       if (count > 0) {
00788         v->tile = 0;
00789         int z_dest = GetAircraftFlyingAltitude(v);
00790 
00791         /* Reached altitude? */
00792         if (v->z_pos >= z_dest) {
00793           v->cur_speed = 0;
00794           return true;
00795         }
00796         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00797       }
00798     }
00799     return false;
00800   }
00801 
00802   /* Helicopter landing. */
00803   if (amd.flag & AMED_HELI_LOWER) {
00804     if (st == NULL) {
00805       /* FIXME - AircraftController -> if station no longer exists, do not land
00806        * helicopter will circle until sign disappears, then go to next order
00807        * what to do when it is the only order left, right now it just stays in 1 place */
00808       v->state = FLYING;
00809       UpdateAircraftCache(v);
00810       AircraftNextAirportPos_and_Order(v);
00811       return false;
00812     }
00813 
00814     /* Vehicle is now at the airport. */
00815     v->tile = tile;
00816 
00817     /* Find altitude of landing position. */
00818     int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00819 
00820     if (z == v->z_pos) {
00821       Vehicle *u = v->Next()->Next();
00822 
00823       /*  Increase speed of rotors. When speed is 80, we've landed. */
00824       if (u->cur_speed >= 80) return true;
00825       u->cur_speed += 4;
00826     } else {
00827       count = UpdateAircraftSpeed(v);
00828       if (count > 0) {
00829         if (v->z_pos > z) {
00830           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00831         } else {
00832           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00833         }
00834       }
00835     }
00836     return false;
00837   }
00838 
00839   /* Get distance from destination pos to current pos. */
00840   uint dist = abs(x + amd.x - v->x_pos) +  abs(y + amd.y - v->y_pos);
00841 
00842   /* Need exact position? */
00843   if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00844 
00845   /* At final pos? */
00846   if (dist == 0) {
00847     /* Change direction smoothly to final direction. */
00848     DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00849     /* if distance is 0, and plane points in right direction, no point in calling
00850      * UpdateAircraftSpeed(). So do it only afterwards */
00851     if (dirdiff == DIRDIFF_SAME) {
00852       v->cur_speed = 0;
00853       return true;
00854     }
00855 
00856     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00857 
00858     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00859     v->cur_speed >>= 1;
00860 
00861     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00862     return false;
00863   }
00864 
00865   if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00866     MaybeCrashAirplane(v);
00867     if ((v->vehstatus & VS_CRASHED) != 0) return false;
00868   }
00869 
00870   uint speed_limit = SPEED_LIMIT_TAXI;
00871   bool hard_limit = true;
00872 
00873   if (amd.flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
00874   if (amd.flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
00875   if (amd.flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00876   if (amd.flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
00877 
00878   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00879   if (count == 0) return false;
00880 
00881   if (v->turn_counter != 0) v->turn_counter--;
00882 
00883   do {
00884 
00885     GetNewVehiclePosResult gp;
00886 
00887     if (dist < 4 || (amd.flag & AMED_LAND)) {
00888       /* move vehicle one pixel towards target */
00889       gp.x = (v->x_pos != (x + amd.x)) ?
00890           v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00891           v->x_pos;
00892       gp.y = (v->y_pos != (y + amd.y)) ?
00893           v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00894           v->y_pos;
00895 
00896       /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
00897       gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00898 
00899     } else {
00900 
00901       /* Turn. Do it slowly if in the air. */
00902       Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00903       if (newdir != v->direction) {
00904         if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00905           if (v->turn_counter == 0 || newdir == v->last_direction) {
00906             if (newdir == v->last_direction) {
00907               v->number_consecutive_turns = 0;
00908             } else {
00909               v->number_consecutive_turns++;
00910             }
00911             v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00912             v->last_direction = v->direction;
00913             v->direction = newdir;
00914           }
00915 
00916           /* Move vehicle. */
00917           gp = GetNewVehiclePos(v);
00918         } else {
00919           v->cur_speed >>= 1;
00920           v->direction = newdir;
00921 
00922           /* When leaving a terminal an aircraft often goes to a position
00923            * directly in front of it. If it would move while turning it
00924            * would need an two extra turns to end up at the correct position.
00925            * To make it easier just disallow all moving while turning as
00926            * long as an aircraft is on the ground. */
00927           gp.x = v->x_pos;
00928           gp.y = v->y_pos;
00929           gp.new_tile = gp.old_tile = v->tile;
00930         }
00931       } else {
00932         v->number_consecutive_turns = 0;
00933         /* Move vehicle. */
00934         gp = GetNewVehiclePos(v);
00935       }
00936     }
00937 
00938     v->tile = gp.new_tile;
00939     /* If vehicle is in the air, use tile coordinate 0. */
00940     if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00941 
00942     /* Adjust Z for land or takeoff? */
00943     int z = v->z_pos;
00944 
00945     if (amd.flag & AMED_TAKEOFF) {
00946       z = min(z + 2, GetAircraftFlyingAltitude(v));
00947     }
00948 
00949     /* Let the plane drop from normal flight altitude to holding pattern altitude */
00950     if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00951 
00952     if (amd.flag & AMED_LAND) {
00953       if (st->airport.tile == INVALID_TILE) {
00954         /* Airport has been removed, abort the landing procedure */
00955         v->state = FLYING;
00956         UpdateAircraftCache(v);
00957         AircraftNextAirportPos_and_Order(v);
00958         /* get aircraft back on running altitude */
00959         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00960         continue;
00961       }
00962 
00963       int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00964 
00965       /* We're not flying below our destination, right? */
00966       assert(curz <= z);
00967       int t = max(1U, dist - 4);
00968       int delta = z - curz;
00969 
00970       /* Only start lowering when we're sufficiently close for a 1:1 glide */
00971       if (delta >= t) {
00972         z -= CeilDiv(z - curz, t);
00973       }
00974       if (z < curz) z = curz;
00975     }
00976 
00977     /* We've landed. Decrease speed when we're reaching end of runway. */
00978     if (amd.flag & AMED_BRAKE) {
00979       int curz = GetSlopePixelZ(x, y) + 1;
00980 
00981       if (z > curz) {
00982         z--;
00983       } else if (z < curz) {
00984         z++;
00985       }
00986 
00987     }
00988 
00989     SetAircraftPosition(v, gp.x, gp.y, z);
00990   } while (--count != 0);
00991   return false;
00992 }
00993 
00998 static bool HandleCrashedAircraft(Aircraft *v)
00999 {
01000   v->crashed_counter += 3;
01001 
01002   Station *st = GetTargetAirportIfValid(v);
01003 
01004   /* make aircraft crash down to the ground */
01005   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01006     int z = GetSlopePixelZ(v->x_pos, v->y_pos);
01007     v->z_pos -= 1;
01008     if (v->z_pos == z) {
01009       v->crashed_counter = 500;
01010       v->z_pos++;
01011     }
01012   }
01013 
01014   if (v->crashed_counter < 650) {
01015     uint32 r;
01016     if (Chance16R(1, 32, r)) {
01017       static const DirDiff delta[] = {
01018         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01019       };
01020 
01021       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01022       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01023       r = Random();
01024       CreateEffectVehicleRel(v,
01025         GB(r, 0, 4) - 4,
01026         GB(r, 4, 4) - 4,
01027         GB(r, 8, 4),
01028         EV_EXPLOSION_SMALL);
01029     }
01030   } else if (v->crashed_counter >= 10000) {
01031     /*  remove rubble of crashed airplane */
01032 
01033     /* clear runway-in on all airports, set by crashing plane
01034      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01035      * but they all share the same number */
01036     if (st != NULL) {
01037       CLRBITS(st->airport.flags, RUNWAY_IN_block);
01038       CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
01039       CLRBITS(st->airport.flags, RUNWAY_IN2_block);    // intercontinental
01040     }
01041 
01042     delete v;
01043 
01044     return false;
01045   }
01046 
01047   return true;
01048 }
01049 
01050 
01051 static void HandleAircraftSmoke(Aircraft *v)
01052 {
01053   static const struct {
01054     int8 x;
01055     int8 y;
01056   } smoke_pos[] = {
01057     {  5,  5 },
01058     {  6,  0 },
01059     {  5, -5 },
01060     {  0, -6 },
01061     { -5, -5 },
01062     { -6,  0 },
01063     { -5,  5 },
01064     {  0,  6 }
01065   };
01066 
01067   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01068 
01069   if (v->cur_speed < 10) {
01070     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01071     v->breakdown_ctr = 0;
01072     return;
01073   }
01074 
01075   if ((v->tick_counter & 0x1F) == 0) {
01076     CreateEffectVehicleRel(v,
01077       smoke_pos[v->direction].x,
01078       smoke_pos[v->direction].y,
01079       2,
01080       EV_BREAKDOWN_SMOKE_AIRCRAFT
01081     );
01082   }
01083 }
01084 
01085 void HandleMissingAircraftOrders(Aircraft *v)
01086 {
01087   /*
01088    * We do not have an order. This can be divided into two cases:
01089    * 1) we are heading to an invalid station. In this case we must
01090    *    find another airport to go to. If there is nowhere to go,
01091    *    we will destroy the aircraft as it otherwise will enter
01092    *    the holding pattern for the first airport, which can cause
01093    *    the plane to go into an undefined state when building an
01094    *    airport with the same StationID.
01095    * 2) we are (still) heading to a (still) valid airport, then we
01096    *    can continue going there. This can happen when you are
01097    *    changing the aircraft's orders while in-flight or in for
01098    *    example a depot. However, when we have a current order to
01099    *    go to a depot, we have to keep that order so the aircraft
01100    *    actually stops.
01101    */
01102   const Station *st = GetTargetAirportIfValid(v);
01103   if (st == NULL) {
01104     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01105     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01106     cur_company.Restore();
01107 
01108     if (ret.Failed()) CrashAirplane(v);
01109   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01110     v->current_order.Free();
01111   }
01112 }
01113 
01114 
01115 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01116 {
01117   /* Orders are changed in flight, ensure going to the right station. */
01118   if (this->state == FLYING) {
01119     AircraftNextAirportPos_and_Order(this);
01120   }
01121 
01122   /* Aircraft do not use dest-tile */
01123   return 0;
01124 }
01125 
01126 void Aircraft::MarkDirty()
01127 {
01128   this->UpdateViewport(false, false);
01129   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01130 }
01131 
01132 
01133 uint Aircraft::Crash(bool flooded)
01134 {
01135   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01136   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01137 
01138   return pass;
01139 }
01140 
01145 static void CrashAirplane(Aircraft *v)
01146 {
01147   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01148 
01149   uint pass = v->Crash();
01150   SetDParam(0, pass);
01151 
01152   v->cargo.Truncate(0);
01153   v->Next()->cargo.Truncate(0);
01154   const Station *st = GetTargetAirportIfValid(v);
01155   StringID newsitem;
01156   if (st == NULL) {
01157     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01158   } else {
01159     SetDParam(1, st->index);
01160     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01161   }
01162 
01163   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01164 
01165   AddVehicleNewsItem(newsitem,
01166     NS_ACCIDENT,
01167     v->index,
01168     st != NULL ? st->index : INVALID_STATION);
01169 
01170   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01171   SndPlayVehicleFx(SND_12_EXPLOSION, v);
01172 }
01173 
01178 static void MaybeCrashAirplane(Aircraft *v)
01179 {
01180   if (_settings_game.vehicle.plane_crashes == 0) return;
01181 
01182   Station *st = Station::Get(v->targetairport);
01183 
01184   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01185   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01186   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01187       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01188       !_cheats.no_jetcrash.value) {
01189     prob /= 20;
01190   } else {
01191     prob /= 1500;
01192   }
01193 
01194   if (GB(Random(), 0, 22) > prob) return;
01195 
01196   /* Crash the airplane. Remove all goods stored at the station. */
01197   for (CargoID i = 0; i < NUM_CARGO; i++) {
01198     st->goods[i].rating = 1;
01199     st->goods[i].cargo.Truncate(0);
01200   }
01201 
01202   CrashAirplane(v);
01203 }
01204 
01210 static void AircraftEntersTerminal(Aircraft *v)
01211 {
01212   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01213 
01214   Station *st = Station::Get(v->targetairport);
01215   v->last_station_visited = v->targetairport;
01216 
01217   /* Check if station was ever visited before */
01218   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01219     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01220     SetDParam(0, st->index);
01221     /* show newsitem of celebrating citizens */
01222     AddVehicleNewsItem(
01223       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01224       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01225       v->index,
01226       st->index
01227     );
01228     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01229   }
01230 
01231   v->BeginLoading();
01232 }
01233 
01238 static void AircraftLandAirplane(Aircraft *v)
01239 {
01240   v->UpdateDeltaXY(INVALID_DIR);
01241 
01242   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01243     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01244   }
01245 }
01246 
01247 
01249 void AircraftNextAirportPos_and_Order(Aircraft *v)
01250 {
01251   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01252     v->targetairport = v->current_order.GetDestination();
01253   }
01254 
01255   const Station *st = GetTargetAirportIfValid(v);
01256   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01257   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01258   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01259 }
01260 
01269 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01270 {
01271   v->cur_speed = 0;
01272   v->subspeed = 0;
01273   v->progress = 0;
01274   v->direction = exit_dir;
01275   v->vehstatus &= ~VS_HIDDEN;
01276   {
01277     Vehicle *u = v->Next();
01278     u->vehstatus &= ~VS_HIDDEN;
01279 
01280     /* Rotor blades */
01281     u = u->Next();
01282     if (u != NULL) {
01283       u->vehstatus &= ~VS_HIDDEN;
01284       u->cur_speed = 80;
01285     }
01286   }
01287 
01288   VehicleServiceInDepot(v);
01289   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01290   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01291   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01292 }
01293 
01297 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01298 {
01299   AircraftEntersTerminal(v);
01300   v->state = apc->layout[v->pos].heading;
01301 }
01302 
01308 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01309 {
01310   VehicleEnterDepot(v);
01311   v->state = apc->layout[v->pos].heading;
01312 }
01313 
01319 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01320 {
01321   /* if we just arrived, execute EnterHangar first */
01322   if (v->previous_pos != v->pos) {
01323     AircraftEventHandler_EnterHangar(v, apc);
01324     return;
01325   }
01326 
01327   /* if we were sent to the depot, stay there */
01328   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01329     v->current_order.Free();
01330     return;
01331   }
01332 
01333   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01334       !v->current_order.IsType(OT_GOTO_DEPOT))
01335     return;
01336 
01337   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
01338   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01339     VehicleEnterDepot(v);
01340     return;
01341   }
01342 
01343   /* if the block of the next position is busy, stay put */
01344   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01345 
01346   /* We are already at the target airport, we need to find a terminal */
01347   if (v->current_order.GetDestination() == v->targetairport) {
01348     /* FindFreeTerminal:
01349      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01350     if (v->subtype == AIR_HELICOPTER) {
01351       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01352     } else {
01353       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01354     }
01355   } else { // Else prepare for launch.
01356     /* airplane goto state takeoff, helicopter to helitakeoff */
01357     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01358   }
01359   const Station *st = Station::GetByTile(v->tile);
01360   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01361   AirportMove(v, apc);
01362 }
01363 
01365 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01366 {
01367   /* if we just arrived, execute EnterTerminal first */
01368   if (v->previous_pos != v->pos) {
01369     AircraftEventHandler_EnterTerminal(v, apc);
01370     /* on an airport with helipads, a helicopter will always land there
01371      * and get serviced at the same time - setting */
01372     if (_settings_game.order.serviceathelipad) {
01373       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01374         /* an exerpt of ServiceAircraft, without the invisibility stuff */
01375         v->date_of_last_service = _date;
01376         v->breakdowns_since_last_service = 0;
01377         v->reliability = v->GetEngine()->reliability;
01378         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01379       }
01380     }
01381     return;
01382   }
01383 
01384   if (v->current_order.IsType(OT_NOTHING)) return;
01385 
01386   /* if the block of the next position is busy, stay put */
01387   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01388 
01389   /* airport-road is free. We either have to go to another airport, or to the hangar
01390    * ---> start moving */
01391 
01392   bool go_to_hangar = false;
01393   switch (v->current_order.GetType()) {
01394     case OT_GOTO_STATION: // ready to fly to another airport
01395       break;
01396     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
01397       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01398       break;
01399     case OT_CONDITIONAL:
01400       /* In case of a conditional order we just have to wait a tick
01401        * longer, so the conditional order can actually be processed;
01402        * we should not clear the order as that makes us go nowhere. */
01403       return;
01404     default:  // orders have been deleted (no orders), goto depot and don't bother us
01405       v->current_order.Free();
01406       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01407   }
01408 
01409   if (go_to_hangar) {
01410     v->state = HANGAR;
01411   } else {
01412     /* airplane goto state takeoff, helicopter to helitakeoff */
01413     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01414   }
01415   AirportMove(v, apc);
01416 }
01417 
01418 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01419 {
01420   error("OK, you shouldn't be here, check your Airport Scheme!");
01421 }
01422 
01423 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01424 {
01425   PlayAircraftSound(v); // play takeoffsound for airplanes
01426   v->state = STARTTAKEOFF;
01427 }
01428 
01429 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01430 {
01431   v->state = ENDTAKEOFF;
01432   v->UpdateDeltaXY(INVALID_DIR);
01433 }
01434 
01435 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01436 {
01437   v->state = FLYING;
01438   /* get the next position to go to, differs per airport */
01439   AircraftNextAirportPos_and_Order(v);
01440 }
01441 
01442 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01443 {
01444   v->state = FLYING;
01445   v->UpdateDeltaXY(INVALID_DIR);
01446 
01447   /* get the next position to go to, differs per airport */
01448   AircraftNextAirportPos_and_Order(v);
01449 
01450   /* Send the helicopter to a hangar if needed for replacement */
01451   if (v->NeedsAutomaticServicing()) {
01452     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01453     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01454     cur_company.Restore();
01455   }
01456 }
01457 
01458 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01459 {
01460   Station *st = Station::Get(v->targetairport);
01461 
01462   /* runway busy or not allowed to use this airstation, circle */
01463   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01464     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01465      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01466      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01467     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01468     const AirportFTA *current = apc->layout[v->pos].next;
01469     while (current != NULL) {
01470       if (current->heading == landingtype) {
01471         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01472          * we don't want that for plane in air
01473          * hack for speed thingie */
01474         uint16 tcur_speed = v->cur_speed;
01475         uint16 tsubspeed = v->subspeed;
01476         if (!AirportHasBlock(v, current, apc)) {
01477           v->state = landingtype; // LANDING / HELILANDING
01478           /* it's a bit dirty, but I need to set position to next position, otherwise
01479            * if there are multiple runways, plane won't know which one it took (because
01480            * they all have heading LANDING). And also occupy that block! */
01481           v->pos = current->next_position;
01482           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01483           return;
01484         }
01485         v->cur_speed = tcur_speed;
01486         v->subspeed = tsubspeed;
01487       }
01488       current = current->next;
01489     }
01490   }
01491   v->state = FLYING;
01492   v->pos = apc->layout[v->pos].next_position;
01493 }
01494 
01495 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01496 {
01497   v->state = ENDLANDING;
01498   AircraftLandAirplane(v);  // maybe crash airplane
01499 
01500   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01501   if (v->NeedsAutomaticServicing()) {
01502     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01503     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01504     cur_company.Restore();
01505   }
01506 }
01507 
01508 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01509 {
01510   v->state = HELIENDLANDING;
01511   v->UpdateDeltaXY(INVALID_DIR);
01512 }
01513 
01514 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01515 {
01516   /* next block busy, don't do a thing, just wait */
01517   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01518 
01519   /* if going to terminal (OT_GOTO_STATION) choose one
01520    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01521    * 2. not going for terminal (but depot, no order),
01522    * --> get out of the way to the hangar. */
01523   if (v->current_order.IsType(OT_GOTO_STATION)) {
01524     if (AirportFindFreeTerminal(v, apc)) return;
01525   }
01526   v->state = HANGAR;
01527 
01528 }
01529 
01530 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01531 {
01532   /*  next block busy, don't do a thing, just wait */
01533   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01534 
01535   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01536    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01537    * 2. not going for terminal (but depot, no order),
01538    * --> get out of the way to the hangar IF there are terminals on the airport.
01539    * --> else TAKEOFF
01540    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01541    * must go to a hangar. */
01542   if (v->current_order.IsType(OT_GOTO_STATION)) {
01543     if (AirportFindFreeHelipad(v, apc)) return;
01544   }
01545   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01546 }
01547 
01553 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01555 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01556   AircraftEventHandler_General,        // TO_ALL         =  0
01557   AircraftEventHandler_InHangar,       // HANGAR         =  1
01558   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01559   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01560   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01561   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01562   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01563   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01564   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01565   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01566   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01567   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01568   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01569   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01570   AircraftEventHandler_Flying,         // FLYING         = 14
01571   AircraftEventHandler_Landing,        // LANDING        = 15
01572   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01573   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01574   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01575   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01576   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01577   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01578 };
01579 
01580 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01581 {
01582   /* we have left the previous block, and entered the new one. Free the previous block */
01583   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01584     Station *st = Station::Get(v->targetairport);
01585 
01586     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01587   }
01588 }
01589 
01590 static void AirportGoToNextPosition(Aircraft *v)
01591 {
01592   /* if aircraft is not in position, wait until it is */
01593   if (!AircraftController(v)) return;
01594 
01595   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01596 
01597   AirportClearBlock(v, apc);
01598   AirportMove(v, apc); // move aircraft to next position
01599 }
01600 
01601 /* gets pos from vehicle and next orders */
01602 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01603 {
01604   /* error handling */
01605   if (v->pos >= apc->nofelements) {
01606     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01607     assert(v->pos < apc->nofelements);
01608   }
01609 
01610   const AirportFTA *current = &apc->layout[v->pos];
01611   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01612   if (current->heading == v->state) {
01613     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01614     byte prev_state = v->state;
01615     _aircraft_state_handlers[v->state](v, apc);
01616     if (v->state != FLYING) v->previous_pos = prev_pos;
01617     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01618     return true;
01619   }
01620 
01621   v->previous_pos = v->pos; // save previous location
01622 
01623   /* there is only one choice to move to */
01624   if (current->next == NULL) {
01625     if (AirportSetBlocks(v, current, apc)) {
01626       v->pos = current->next_position;
01627       UpdateAircraftCache(v);
01628     } // move to next position
01629     return false;
01630   }
01631 
01632   /* there are more choices to choose from, choose the one that
01633    * matches our heading */
01634   do {
01635     if (v->state == current->heading || current->heading == TO_ALL) {
01636       if (AirportSetBlocks(v, current, apc)) {
01637         v->pos = current->next_position;
01638         UpdateAircraftCache(v);
01639       } // move to next position
01640       return false;
01641     }
01642     current = current->next;
01643   } while (current != NULL);
01644 
01645   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01646   NOT_REACHED();
01647 }
01648 
01650 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01651 {
01652   const AirportFTA *reference = &apc->layout[v->pos];
01653   const AirportFTA *next = &apc->layout[current_pos->next_position];
01654 
01655   /* same block, then of course we can move */
01656   if (apc->layout[current_pos->position].block != next->block) {
01657     const Station *st = Station::Get(v->targetairport);
01658     uint64 airport_flags = next->block;
01659 
01660     /* check additional possible extra blocks */
01661     if (current_pos != reference && current_pos->block != NOTHING_block) {
01662       airport_flags |= current_pos->block;
01663     }
01664 
01665     if (st->airport.flags & airport_flags) {
01666       v->cur_speed = 0;
01667       v->subspeed = 0;
01668       return true;
01669     }
01670   }
01671   return false;
01672 }
01673 
01681 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01682 {
01683   const AirportFTA *next = &apc->layout[current_pos->next_position];
01684   const AirportFTA *reference = &apc->layout[v->pos];
01685 
01686   /* if the next position is in another block, check it and wait until it is free */
01687   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01688     uint64 airport_flags = next->block;
01689     /* search for all all elements in the list with the same state, and blocks != N
01690      * this means more blocks should be checked/set */
01691     const AirportFTA *current = current_pos;
01692     if (current == reference) current = current->next;
01693     while (current != NULL) {
01694       if (current->heading == current_pos->heading && current->block != 0) {
01695         airport_flags |= current->block;
01696         break;
01697       }
01698       current = current->next;
01699     }
01700 
01701     /* if the block to be checked is in the next position, then exclude that from
01702      * checking, because it has been set by the airplane before */
01703     if (current_pos->block == next->block) airport_flags ^= next->block;
01704 
01705     Station *st = Station::Get(v->targetairport);
01706     if (st->airport.flags & airport_flags) {
01707       v->cur_speed = 0;
01708       v->subspeed = 0;
01709       return false;
01710     }
01711 
01712     if (next->block != NOTHING_block) {
01713       SETBITS(st->airport.flags, airport_flags); // occupy next block
01714     }
01715   }
01716   return true;
01717 }
01718 
01723 struct MovementTerminalMapping {
01724   AirportMovementStates state; 
01725   uint64 airport_flag;         
01726 };
01727 
01729 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01730   {TERM1, TERM1_block},
01731   {TERM2, TERM2_block},
01732   {TERM3, TERM3_block},
01733   {TERM4, TERM4_block},
01734   {TERM5, TERM5_block},
01735   {TERM6, TERM6_block},
01736   {TERM7, TERM7_block},
01737   {TERM8, TERM8_block},
01738   {HELIPAD1, HELIPAD1_block},
01739   {HELIPAD2, HELIPAD2_block},
01740   {HELIPAD3, HELIPAD3_block},
01741 };
01742 
01750 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01751 {
01752   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01753   Station *st = Station::Get(v->targetairport);
01754   for (; i < last_terminal; i++) {
01755     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01756       /* TERMINAL# HELIPAD# */
01757       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01758       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01759       return true;
01760     }
01761   }
01762   return false;
01763 }
01764 
01770 static uint GetNumTerminals(const AirportFTAClass *apc)
01771 {
01772   uint num = 0;
01773 
01774   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01775 
01776   return num;
01777 }
01778 
01785 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01786 {
01787   /* example of more terminalgroups
01788    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01789    * Heading 255 denotes a group. We see 2 groups here:
01790    * 1. group 0 -- TERM_GROUP1_block (check block)
01791    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01792    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01793    * looks at the corresponding terminals of that group. If no free ones are found, other
01794    * possible groups are checked (in this case group 1, since that is after group 0). If that
01795    * fails, then attempt fails and plane waits
01796    */
01797   if (apc->terminals[0] > 1) {
01798     const Station *st = Station::Get(v->targetairport);
01799     const AirportFTA *temp = apc->layout[v->pos].next;
01800 
01801     while (temp != NULL) {
01802       if (temp->heading == 255) {
01803         if (!(st->airport.flags & temp->block)) {
01804           /* read which group do we want to go to?
01805            * (the first free group) */
01806           uint target_group = temp->next_position + 1;
01807 
01808           /* at what terminal does the group start?
01809            * that means, sum up all terminals of
01810            * groups with lower number */
01811           uint group_start = 0;
01812           for (uint i = 1; i < target_group; i++) {
01813             group_start += apc->terminals[i];
01814           }
01815 
01816           uint group_end = group_start + apc->terminals[target_group];
01817           if (FreeTerminal(v, group_start, group_end)) return true;
01818         }
01819       } else {
01820         /* once the heading isn't 255, we've exhausted the possible blocks.
01821          * So we cannot move */
01822         return false;
01823       }
01824       temp = temp->next;
01825     }
01826   }
01827 
01828   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01829   return FreeTerminal(v, 0, GetNumTerminals(apc));
01830 }
01831 
01838 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01839 {
01840   /* if an airport doesn't have helipads, use terminals */
01841   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01842 
01843   /* only 1 helicoptergroup, check all helipads
01844    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01845   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01846 }
01847 
01853 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01854 {
01855   if (too_far) {
01856     if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01857       SetBit(v->flags, VAF_DEST_TOO_FAR);
01858       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01859       AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01860       if (v->owner == _local_company) {
01861         /* Post a news message. */
01862         SetDParam(0, v->index);
01863         AddVehicleNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, NS_ADVICE, v->index);
01864       }
01865     }
01866     return;
01867   }
01868 
01869   if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01870     /* Not too far anymore, clear flag and message. */
01871     ClrBit(v->flags, VAF_DEST_TOO_FAR);
01872     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01873     DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01874   }
01875 }
01876 
01877 static bool AircraftEventHandler(Aircraft *v, int loop)
01878 {
01879   v->tick_counter++;
01880 
01881   if (v->vehstatus & VS_CRASHED) {
01882     return HandleCrashedAircraft(v);
01883   }
01884 
01885   if (v->vehstatus & VS_STOPPED) return true;
01886 
01887   v->HandleBreakdown();
01888 
01889   HandleAircraftSmoke(v);
01890   ProcessOrders(v);
01891   v->HandleLoading(loop != 0);
01892 
01893   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01894 
01895   if (v->state == FLYING) {
01896     /* If we are flying, unconditionally clear the 'dest too far' state. */
01897     AircraftHandleDestTooFar(v, false);
01898   } else if (v->acache.cached_max_range_sqr != 0) {
01899     /* Check the distance to the next destination. This code works because the target
01900      * airport is only updated after take off and not on the ground. */
01901     Station *cur_st = Station::GetIfValid(v->targetairport);
01902     Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01903 
01904     if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01905       uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01906       AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01907     }
01908   }
01909 
01910   if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01911 
01912   return true;
01913 }
01914 
01915 bool Aircraft::Tick()
01916 {
01917   if (!this->IsNormalAircraft()) return true;
01918 
01919   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01920 
01921   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01922 
01923   this->current_order_time++;
01924 
01925   for (uint i = 0; i != 2; i++) {
01926     /* stop if the aircraft was deleted */
01927     if (!AircraftEventHandler(this, i)) return false;
01928   }
01929 
01930   return true;
01931 }
01932 
01933 
01940 Station *GetTargetAirportIfValid(const Aircraft *v)
01941 {
01942   assert(v->type == VEH_AIRCRAFT);
01943 
01944   Station *st = Station::GetIfValid(v->targetairport);
01945   if (st == NULL) return NULL;
01946 
01947   return st->airport.tile == INVALID_TILE ? NULL : st;
01948 }
01949 
01954 void UpdateAirplanesOnNewStation(const Station *st)
01955 {
01956   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01957   const AirportFTAClass *ap = st->airport.GetFTA();
01958   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01959 
01960   Aircraft *v;
01961   FOR_ALL_AIRCRAFT(v) {
01962     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01963     assert(v->state == FLYING);
01964     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01965     UpdateAircraftCache(v);
01966   }
01967 }