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 "newgrf_engine.h"
00020 #include "newgrf_sound.h"
00021 #include "spritecache.h"
00022 #include "strings_func.h"
00023 #include "command_func.h"
00024 #include "window_func.h"
00025 #include "date_func.h"
00026 #include "vehicle_func.h"
00027 #include "sound_func.h"
00028 #include "cheat_type.h"
00029 #include "company_base.h"
00030 #include "ai/ai.hpp"
00031 #include "game/game.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     VehicleUpdatePosition(v);
00318     VehicleUpdatePosition(u);
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       VehicleUpdatePosition(w);
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->IsChainInDepot()) {
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, WID_VV_START_STOP);
00386   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00387     v->current_order.MakeDummy();
00388     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
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   VehicleUpdatePositionAndViewport(u);
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   VehicleUpdatePosition(v);
00484   v->UpdateViewport(true, false);
00485   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
00486 
00487   Aircraft *u = v->Next();
00488 
00489   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00490   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00491   u->x_pos = x;
00492   u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
00493 
00494   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00495   u->z_pos = GetSlopePixelZ(safe_x, safe_y);
00496   u->cur_image = v->cur_image;
00497 
00498   VehicleUpdatePositionAndViewport(u);
00499 
00500   u = u->Next();
00501   if (u != NULL) {
00502     u->x_pos = x;
00503     u->y_pos = y;
00504     u->z_pos = z + ROTOR_Z_OFFSET;
00505 
00506     VehicleUpdatePositionAndViewport(u);
00507   }
00508 }
00509 
00514 void HandleAircraftEnterHangar(Aircraft *v)
00515 {
00516   v->subspeed = 0;
00517   v->progress = 0;
00518 
00519   Aircraft *u = v->Next();
00520   u->vehstatus |= VS_HIDDEN;
00521   u = u->Next();
00522   if (u != NULL) {
00523     u->vehstatus |= VS_HIDDEN;
00524     u->cur_speed = 0;
00525   }
00526 
00527   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00528 }
00529 
00530 static void PlayAircraftSound(const Vehicle *v)
00531 {
00532   if (!PlayVehicleSound(v, VSE_START)) {
00533     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00534   }
00535 }
00536 
00537 
00544 void UpdateAircraftCache(Aircraft *v, bool update_range)
00545 {
00546   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00547   if (max_speed != 0) {
00548     /* Convert from original units to km-ish/h */
00549     max_speed = (max_speed * 128) / 10;
00550 
00551     v->vcache.cached_max_speed = max_speed;
00552   } else {
00553     /* Use the default max speed of the vehicle. */
00554     v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00555   }
00556 
00557   /* Update cargo aging period. */
00558   v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00559   Aircraft *u = v->Next(); // Shadow for mail
00560   u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00561 
00562   /* Update aircraft range. */
00563   if (update_range) {
00564     v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
00565     /* Squared it now so we don't have to do it later all the time. */
00566     v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
00567   }
00568 }
00569 
00570 
00574 enum AircraftSpeedLimits {
00575   SPEED_LIMIT_TAXI     =     50,  
00576   SPEED_LIMIT_APPROACH =    230,  
00577   SPEED_LIMIT_BROKEN   =    320,  
00578   SPEED_LIMIT_HOLD     =    425,  
00579   SPEED_LIMIT_NONE     = 0xFFFF,  
00580 };
00581 
00589 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00590 {
00591   uint spd = v->acceleration * 16;
00592   byte t;
00593 
00594   /* Adjust speed limits by plane speed factor to prevent taxiing
00595    * and take-off speeds being too low. */
00596   speed_limit *= _settings_game.vehicle.plane_speed;
00597 
00598   if (v->vcache.cached_max_speed < speed_limit) {
00599     if (v->cur_speed < speed_limit) hard_limit = false;
00600     speed_limit = v->vcache.cached_max_speed;
00601   }
00602 
00603   v->subspeed = (t = v->subspeed) + (byte)spd;
00604 
00605   /* Aircraft's current speed is used twice so that very fast planes are
00606    * forced to slow down rapidly in the short distance needed. The magic
00607    * value 16384 was determined to give similar results to the old speed/48
00608    * method at slower speeds. This also results in less reduction at slow
00609    * speeds to that aircraft do not get to taxi speed straight after
00610    * touchdown. */
00611   if (!hard_limit && v->cur_speed > speed_limit) {
00612     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00613   }
00614 
00615   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00616 
00617   /* adjust speed for broken vehicles */
00618   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00619 
00620   /* updates statusbar only if speed have changed to save CPU time */
00621   if (spd != v->cur_speed) {
00622     v->cur_speed = spd;
00623     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00624   }
00625 
00626   /* Adjust distance moved by plane speed setting */
00627   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00628 
00629   /* Convert direction-indepenent speed into direction-dependent speed. (old movement method) */
00630   spd = v->GetOldAdvanceSpeed(spd);
00631 
00632   spd += v->progress;
00633   v->progress = (byte)spd;
00634   return spd >> 8;
00635 }
00636 
00644 int GetAircraftFlyingAltitude(const Aircraft *v)
00645 {
00646   if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00647 
00648   /* Make sure Aircraft fly no lower so that they don't conduct
00649    * CFITs (controlled flight into terrain)
00650    */
00651   int base_altitude = PLANE_HOLDING_ALTITUDE;
00652 
00653   /* Make sure eastbound and westbound planes do not "crash" into each
00654    * other by providing them with vertical seperation
00655    */
00656   switch (v->direction) {
00657     case DIR_N:
00658     case DIR_NE:
00659     case DIR_E:
00660     case DIR_SE:
00661       base_altitude += 10;
00662       break;
00663 
00664     default: break;
00665   }
00666 
00667   /* Make faster planes fly higher so that they can overtake slower ones */
00668   base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00669 
00670   return base_altitude;
00671 }
00672 
00687 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00688 {
00689   assert(v != NULL);
00690   assert(apc != NULL);
00691 
00692   /* In the case the station doesn't exit anymore, set target tile 0.
00693    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00694    * or it will simply crash in next tick */
00695   TileIndex tile = 0;
00696 
00697   const Station *st = Station::GetIfValid(v->targetairport);
00698   if (st != NULL) {
00699     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00700     tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00701   }
00702 
00703   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00704   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00705 
00706   DiagDirection dir;
00707   if (abs(delta_y) < abs(delta_x)) {
00708     /* We are northeast or southwest of the airport */
00709     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00710   } else {
00711     /* We are northwest or southeast of the airport */
00712     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00713   }
00714   dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00715   return apc->entry_points[dir];
00716 }
00717 
00718 
00719 static void MaybeCrashAirplane(Aircraft *v);
00720 
00728 static bool AircraftController(Aircraft *v)
00729 {
00730   int count;
00731 
00732   /* NULL if station is invalid */
00733   const Station *st = Station::GetIfValid(v->targetairport);
00734   /* INVALID_TILE if there is no station */
00735   TileIndex tile = INVALID_TILE;
00736   Direction rotation = DIR_N;
00737   uint size_x = 1, size_y = 1;
00738   if (st != NULL) {
00739     if (st->airport.tile != INVALID_TILE) {
00740       tile = st->airport.tile;
00741       rotation = st->airport.rotation;
00742       size_x = st->airport.w;
00743       size_y = st->airport.h;
00744     } else {
00745       tile = st->xy;
00746     }
00747   }
00748   /* DUMMY if there is no station or no airport */
00749   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00750 
00751   /* prevent going to INVALID_TILE if airport is deleted. */
00752   if (st == NULL || st->airport.tile == INVALID_TILE) {
00753     /* Jump into our "holding pattern" state machine if possible */
00754     if (v->pos >= afc->nofelements) {
00755       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00756     } else if (v->targetairport != v->current_order.GetDestination()) {
00757       /* If not possible, just get out of here fast */
00758       v->state = FLYING;
00759       UpdateAircraftCache(v);
00760       AircraftNextAirportPos_and_Order(v);
00761       /* get aircraft back on running altitude */
00762       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00763       return false;
00764     }
00765   }
00766 
00767   /*  get airport moving data */
00768   const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00769 
00770   int x = TileX(tile) * TILE_SIZE;
00771   int y = TileY(tile) * TILE_SIZE;
00772 
00773   /* Helicopter raise */
00774   if (amd.flag & AMED_HELI_RAISE) {
00775     Aircraft *u = v->Next()->Next();
00776 
00777     /* Make sure the rotors don't rotate too fast */
00778     if (u->cur_speed > 32) {
00779       v->cur_speed = 0;
00780       if (--u->cur_speed == 32) {
00781         if (!PlayVehicleSound(v, VSE_START)) {
00782           SndPlayVehicleFx(SND_18_HELICOPTER, v);
00783         }
00784       }
00785     } else {
00786       u->cur_speed = 32;
00787       count = UpdateAircraftSpeed(v);
00788       if (count > 0) {
00789         v->tile = 0;
00790         int z_dest = GetAircraftFlyingAltitude(v);
00791 
00792         /* Reached altitude? */
00793         if (v->z_pos >= z_dest) {
00794           v->cur_speed = 0;
00795           return true;
00796         }
00797         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00798       }
00799     }
00800     return false;
00801   }
00802 
00803   /* Helicopter landing. */
00804   if (amd.flag & AMED_HELI_LOWER) {
00805     if (st == NULL) {
00806       /* FIXME - AircraftController -> if station no longer exists, do not land
00807        * helicopter will circle until sign disappears, then go to next order
00808        * what to do when it is the only order left, right now it just stays in 1 place */
00809       v->state = FLYING;
00810       UpdateAircraftCache(v);
00811       AircraftNextAirportPos_and_Order(v);
00812       return false;
00813     }
00814 
00815     /* Vehicle is now at the airport. */
00816     v->tile = tile;
00817 
00818     /* Find altitude of landing position. */
00819     int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00820 
00821     if (z == v->z_pos) {
00822       Vehicle *u = v->Next()->Next();
00823 
00824       /*  Increase speed of rotors. When speed is 80, we've landed. */
00825       if (u->cur_speed >= 80) return true;
00826       u->cur_speed += 4;
00827     } else {
00828       count = UpdateAircraftSpeed(v);
00829       if (count > 0) {
00830         if (v->z_pos > z) {
00831           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00832         } else {
00833           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00834         }
00835       }
00836     }
00837     return false;
00838   }
00839 
00840   /* Get distance from destination pos to current pos. */
00841   uint dist = abs(x + amd.x - v->x_pos) +  abs(y + amd.y - v->y_pos);
00842 
00843   /* Need exact position? */
00844   if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00845 
00846   /* At final pos? */
00847   if (dist == 0) {
00848     /* Change direction smoothly to final direction. */
00849     DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00850     /* if distance is 0, and plane points in right direction, no point in calling
00851      * UpdateAircraftSpeed(). So do it only afterwards */
00852     if (dirdiff == DIRDIFF_SAME) {
00853       v->cur_speed = 0;
00854       return true;
00855     }
00856 
00857     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00858 
00859     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00860     v->cur_speed >>= 1;
00861 
00862     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00863     return false;
00864   }
00865 
00866   if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00867     MaybeCrashAirplane(v);
00868     if ((v->vehstatus & VS_CRASHED) != 0) return false;
00869   }
00870 
00871   uint speed_limit = SPEED_LIMIT_TAXI;
00872   bool hard_limit = true;
00873 
00874   if (amd.flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
00875   if (amd.flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
00876   if (amd.flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00877   if (amd.flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
00878 
00879   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00880   if (count == 0) return false;
00881 
00882   if (v->turn_counter != 0) v->turn_counter--;
00883 
00884   do {
00885 
00886     GetNewVehiclePosResult gp;
00887 
00888     if (dist < 4 || (amd.flag & AMED_LAND)) {
00889       /* move vehicle one pixel towards target */
00890       gp.x = (v->x_pos != (x + amd.x)) ?
00891           v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00892           v->x_pos;
00893       gp.y = (v->y_pos != (y + amd.y)) ?
00894           v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00895           v->y_pos;
00896 
00897       /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
00898       gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00899 
00900     } else {
00901 
00902       /* Turn. Do it slowly if in the air. */
00903       Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00904       if (newdir != v->direction) {
00905         if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00906           if (v->turn_counter == 0 || newdir == v->last_direction) {
00907             if (newdir == v->last_direction) {
00908               v->number_consecutive_turns = 0;
00909             } else {
00910               v->number_consecutive_turns++;
00911             }
00912             v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00913             v->last_direction = v->direction;
00914             v->direction = newdir;
00915           }
00916 
00917           /* Move vehicle. */
00918           gp = GetNewVehiclePos(v);
00919         } else {
00920           v->cur_speed >>= 1;
00921           v->direction = newdir;
00922 
00923           /* When leaving a terminal an aircraft often goes to a position
00924            * directly in front of it. If it would move while turning it
00925            * would need an two extra turns to end up at the correct position.
00926            * To make it easier just disallow all moving while turning as
00927            * long as an aircraft is on the ground. */
00928           gp.x = v->x_pos;
00929           gp.y = v->y_pos;
00930           gp.new_tile = gp.old_tile = v->tile;
00931         }
00932       } else {
00933         v->number_consecutive_turns = 0;
00934         /* Move vehicle. */
00935         gp = GetNewVehiclePos(v);
00936       }
00937     }
00938 
00939     v->tile = gp.new_tile;
00940     /* If vehicle is in the air, use tile coordinate 0. */
00941     if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00942 
00943     /* Adjust Z for land or takeoff? */
00944     int z = v->z_pos;
00945 
00946     if (amd.flag & AMED_TAKEOFF) {
00947       z = min(z + 2, GetAircraftFlyingAltitude(v));
00948     }
00949 
00950     /* Let the plane drop from normal flight altitude to holding pattern altitude */
00951     if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00952 
00953     if (amd.flag & AMED_LAND) {
00954       if (st->airport.tile == INVALID_TILE) {
00955         /* Airport has been removed, abort the landing procedure */
00956         v->state = FLYING;
00957         UpdateAircraftCache(v);
00958         AircraftNextAirportPos_and_Order(v);
00959         /* get aircraft back on running altitude */
00960         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00961         continue;
00962       }
00963 
00964       int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00965 
00966       /* We're not flying below our destination, right? */
00967       assert(curz <= z);
00968       int t = max(1U, dist - 4);
00969       int delta = z - curz;
00970 
00971       /* Only start lowering when we're sufficiently close for a 1:1 glide */
00972       if (delta >= t) {
00973         z -= CeilDiv(z - curz, t);
00974       }
00975       if (z < curz) z = curz;
00976     }
00977 
00978     /* We've landed. Decrease speed when we're reaching end of runway. */
00979     if (amd.flag & AMED_BRAKE) {
00980       int curz = GetSlopePixelZ(x, y) + 1;
00981 
00982       if (z > curz) {
00983         z--;
00984       } else if (z < curz) {
00985         z++;
00986       }
00987 
00988     }
00989 
00990     SetAircraftPosition(v, gp.x, gp.y, z);
00991   } while (--count != 0);
00992   return false;
00993 }
00994 
00999 static bool HandleCrashedAircraft(Aircraft *v)
01000 {
01001   v->crashed_counter += 3;
01002 
01003   Station *st = GetTargetAirportIfValid(v);
01004 
01005   /* make aircraft crash down to the ground */
01006   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01007     int z = GetSlopePixelZ(v->x_pos, v->y_pos);
01008     v->z_pos -= 1;
01009     if (v->z_pos == z) {
01010       v->crashed_counter = 500;
01011       v->z_pos++;
01012     }
01013   }
01014 
01015   if (v->crashed_counter < 650) {
01016     uint32 r;
01017     if (Chance16R(1, 32, r)) {
01018       static const DirDiff delta[] = {
01019         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01020       };
01021 
01022       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01023       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01024       r = Random();
01025       CreateEffectVehicleRel(v,
01026         GB(r, 0, 4) - 4,
01027         GB(r, 4, 4) - 4,
01028         GB(r, 8, 4),
01029         EV_EXPLOSION_SMALL);
01030     }
01031   } else if (v->crashed_counter >= 10000) {
01032     /*  remove rubble of crashed airplane */
01033 
01034     /* clear runway-in on all airports, set by crashing plane
01035      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01036      * but they all share the same number */
01037     if (st != NULL) {
01038       CLRBITS(st->airport.flags, RUNWAY_IN_block);
01039       CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
01040       CLRBITS(st->airport.flags, RUNWAY_IN2_block);    // intercontinental
01041     }
01042 
01043     delete v;
01044 
01045     return false;
01046   }
01047 
01048   return true;
01049 }
01050 
01051 
01052 static void HandleAircraftSmoke(Aircraft *v)
01053 {
01054   static const struct {
01055     int8 x;
01056     int8 y;
01057   } smoke_pos[] = {
01058     {  5,  5 },
01059     {  6,  0 },
01060     {  5, -5 },
01061     {  0, -6 },
01062     { -5, -5 },
01063     { -6,  0 },
01064     { -5,  5 },
01065     {  0,  6 }
01066   };
01067 
01068   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01069 
01070   if (v->cur_speed < 10) {
01071     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01072     v->breakdown_ctr = 0;
01073     return;
01074   }
01075 
01076   if ((v->tick_counter & 0x1F) == 0) {
01077     CreateEffectVehicleRel(v,
01078       smoke_pos[v->direction].x,
01079       smoke_pos[v->direction].y,
01080       2,
01081       EV_BREAKDOWN_SMOKE_AIRCRAFT
01082     );
01083   }
01084 }
01085 
01086 void HandleMissingAircraftOrders(Aircraft *v)
01087 {
01088   /*
01089    * We do not have an order. This can be divided into two cases:
01090    * 1) we are heading to an invalid station. In this case we must
01091    *    find another airport to go to. If there is nowhere to go,
01092    *    we will destroy the aircraft as it otherwise will enter
01093    *    the holding pattern for the first airport, which can cause
01094    *    the plane to go into an undefined state when building an
01095    *    airport with the same StationID.
01096    * 2) we are (still) heading to a (still) valid airport, then we
01097    *    can continue going there. This can happen when you are
01098    *    changing the aircraft's orders while in-flight or in for
01099    *    example a depot. However, when we have a current order to
01100    *    go to a depot, we have to keep that order so the aircraft
01101    *    actually stops.
01102    */
01103   const Station *st = GetTargetAirportIfValid(v);
01104   if (st == NULL) {
01105     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01106     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01107     cur_company.Restore();
01108 
01109     if (ret.Failed()) CrashAirplane(v);
01110   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01111     v->current_order.Free();
01112   }
01113 }
01114 
01115 
01116 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01117 {
01118   /* Orders are changed in flight, ensure going to the right station. */
01119   if (this->state == FLYING) {
01120     AircraftNextAirportPos_and_Order(this);
01121   }
01122 
01123   /* Aircraft do not use dest-tile */
01124   return 0;
01125 }
01126 
01127 void Aircraft::MarkDirty()
01128 {
01129   this->UpdateViewport(false, false);
01130   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01131 }
01132 
01133 
01134 uint Aircraft::Crash(bool flooded)
01135 {
01136   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01137   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01138 
01139   return pass;
01140 }
01141 
01146 static void CrashAirplane(Aircraft *v)
01147 {
01148   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01149 
01150   uint pass = v->Crash();
01151   SetDParam(0, pass);
01152 
01153   v->cargo.Truncate(0);
01154   v->Next()->cargo.Truncate(0);
01155   const Station *st = GetTargetAirportIfValid(v);
01156   StringID newsitem;
01157   if (st == NULL) {
01158     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01159   } else {
01160     SetDParam(1, st->index);
01161     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01162   }
01163 
01164   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01165   Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01166 
01167   AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
01168 
01169   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01170   SndPlayVehicleFx(SND_12_EXPLOSION, v);
01171 }
01172 
01177 static void MaybeCrashAirplane(Aircraft *v)
01178 {
01179   if (_settings_game.vehicle.plane_crashes == 0) return;
01180 
01181   Station *st = Station::Get(v->targetairport);
01182 
01183   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01184   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01185   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01186       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01187       !_cheats.no_jetcrash.value) {
01188     prob /= 20;
01189   } else {
01190     prob /= 1500;
01191   }
01192 
01193   if (GB(Random(), 0, 22) > prob) return;
01194 
01195   /* Crash the airplane. Remove all goods stored at the station. */
01196   for (CargoID i = 0; i < NUM_CARGO; i++) {
01197     st->goods[i].rating = 1;
01198     st->goods[i].cargo.Truncate(0);
01199   }
01200 
01201   CrashAirplane(v);
01202 }
01203 
01209 static void AircraftEntersTerminal(Aircraft *v)
01210 {
01211   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01212 
01213   Station *st = Station::Get(v->targetairport);
01214   v->last_station_visited = v->targetairport;
01215 
01216   /* Check if station was ever visited before */
01217   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01218     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01219     SetDParam(0, st->index);
01220     /* show newsitem of celebrating citizens */
01221     AddVehicleNewsItem(
01222       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01223       (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
01224       v->index,
01225       st->index
01226     );
01227     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01228     Game::NewEvent(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, not allowed to use this airstation or closed, circle. */
01463   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
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, WID_VV_START_STOP);
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         AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, 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, WID_VV_START_STOP);
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 >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
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 }