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 (v->acache.cached_max_range_sqr != 0) {
00140       /* Check if our current destination can be reached from the depot airport. */
00141       const Station *cur_dest = GetTargetAirportIfValid(v);
00142       if (cur_dest != NULL && DistanceSquare(st->airport.tile, cur_dest->airport.tile) > v->acache.cached_max_range_sqr) continue;
00143     }
00144     if (distance < best || index == INVALID_STATION) {
00145       best = distance;
00146       index = st->index;
00147     }
00148   }
00149   return index;
00150 }
00151 
00152 SpriteID Aircraft::GetImage(Direction direction, EngineImageType image_type) const
00153 {
00154   uint8 spritenum = this->spritenum;
00155 
00156   if (is_custom_sprite(spritenum)) {
00157     SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type);
00158     if (sprite != 0) return sprite;
00159 
00160     spritenum = this->GetEngine()->original_image_index;
00161   }
00162 
00163   return direction + _aircraft_sprite[spritenum];
00164 }
00165 
00166 SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type)
00167 {
00168   assert(v->subtype == AIR_HELICOPTER);
00169 
00170   const Aircraft *w = v->Next()->Next();
00171   if (is_custom_sprite(v->spritenum)) {
00172     SpriteID sprite = GetCustomRotorSprite(v, false, image_type);
00173     if (sprite != 0) return sprite;
00174   }
00175 
00176   /* Return standard rotor sprites if there are no custom sprites for this helicopter */
00177   return SPR_ROTOR_STOPPED + w->state;
00178 }
00179 
00180 static SpriteID GetAircraftIcon(EngineID engine, EngineImageType image_type)
00181 {
00182   const Engine *e = Engine::Get(engine);
00183   uint8 spritenum = e->u.air.image_index;
00184 
00185   if (is_custom_sprite(spritenum)) {
00186     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
00187     if (sprite != 0) return sprite;
00188 
00189     spritenum = e->original_image_index;
00190   }
00191 
00192   return DIR_W + _aircraft_sprite[spritenum];
00193 }
00194 
00195 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
00196 {
00197   SpriteID sprite = GetAircraftIcon(engine, image_type);
00198   const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00199   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));
00200   DrawSprite(sprite, pal, preferred_x, y);
00201 
00202   if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00203     SpriteID rotor_sprite = GetCustomRotorIcon(engine, image_type);
00204     if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00205     DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00206   }
00207 }
00208 
00218 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
00219 {
00220   const Sprite *spr = GetSprite(GetAircraftIcon(engine, image_type), ST_NORMAL);
00221 
00222   width  = UnScaleByZoom(spr->width, ZOOM_LVL_GUI);
00223   height = UnScaleByZoom(spr->height, ZOOM_LVL_GUI);
00224   xoffs  = UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI);
00225   yoffs  = UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI);
00226 }
00227 
00237 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
00238 {
00239   const AircraftVehicleInfo *avi = &e->u.air;
00240   const Station *st = Station::GetByTile(tile);
00241 
00242   /* Prevent building aircraft types at places which can't handle them */
00243   if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
00244 
00245   /* Make sure all aircraft end up in the first tile of the hanger. */
00246   tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
00247 
00248   if (flags & DC_EXEC) {
00249     Aircraft *v = new Aircraft(); // aircraft
00250     Aircraft *u = new Aircraft(); // shadow
00251     *ret = v;
00252 
00253     v->direction = DIR_SE;
00254 
00255     v->owner = u->owner = _current_company;
00256 
00257     v->tile = tile;
00258 
00259     uint x = TileX(tile) * TILE_SIZE + 5;
00260     uint y = TileY(tile) * TILE_SIZE + 3;
00261 
00262     v->x_pos = u->x_pos = x;
00263     v->y_pos = u->y_pos = y;
00264 
00265     u->z_pos = GetSlopePixelZ(x, y);
00266     v->z_pos = u->z_pos + 1;
00267 
00268     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00269     u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00270 
00271     v->spritenum = avi->image_index;
00272 
00273     v->cargo_cap = avi->passenger_capacity;
00274     v->refit_cap = 0;
00275     u->cargo_cap = avi->mail_capacity;
00276     u->refit_cap = 0;
00277 
00278     v->cargo_type = e->GetDefaultCargoType();
00279     u->cargo_type = CT_MAIL;
00280 
00281     v->name = NULL;
00282     v->last_station_visited = INVALID_STATION;
00283     v->last_loading_station = INVALID_STATION;
00284 
00285     v->acceleration = avi->acceleration;
00286     v->engine_type = e->index;
00287     u->engine_type = e->index;
00288 
00289     v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00290     v->UpdateDeltaXY(INVALID_DIR);
00291 
00292     u->subtype = AIR_SHADOW;
00293     u->UpdateDeltaXY(INVALID_DIR);
00294 
00295     v->reliability = e->reliability;
00296     v->reliability_spd_dec = e->reliability_spd_dec;
00297     v->max_age = e->GetLifeLengthInDays();
00298 
00299     _new_vehicle_id = v->index;
00300 
00301     v->pos = GetVehiclePosOnBuild(tile);
00302 
00303     v->state = HANGAR;
00304     v->previous_pos = v->pos;
00305     v->targetairport = GetStationIndex(tile);
00306     v->SetNext(u);
00307 
00308     v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
00309 
00310     v->date_of_last_service = _date;
00311     v->build_year = u->build_year = _cur_year;
00312 
00313     v->cur_image = u->cur_image = SPR_IMG_QUERY;
00314 
00315     v->random_bits = VehicleRandomBits();
00316     u->random_bits = VehicleRandomBits();
00317 
00318     v->vehicle_flags = 0;
00319     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00320     v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
00321 
00322     v->InvalidateNewGRFCacheOfChain();
00323 
00324     v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
00325 
00326     v->InvalidateNewGRFCacheOfChain();
00327 
00328     UpdateAircraftCache(v, true);
00329 
00330     VehicleUpdatePosition(v);
00331     VehicleUpdatePosition(u);
00332 
00333     /* Aircraft with 3 vehicles (chopper)? */
00334     if (v->subtype == AIR_HELICOPTER) {
00335       Aircraft *w = new Aircraft();
00336       w->engine_type = e->index;
00337       w->direction = DIR_N;
00338       w->owner = _current_company;
00339       w->x_pos = v->x_pos;
00340       w->y_pos = v->y_pos;
00341       w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
00342       w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00343       w->spritenum = 0xFF;
00344       w->subtype = AIR_ROTOR;
00345       w->cur_image = SPR_ROTOR_STOPPED;
00346       w->random_bits = VehicleRandomBits();
00347       /* Use rotor's air.state to store the rotor animation frame */
00348       w->state = HRS_ROTOR_STOPPED;
00349       w->UpdateDeltaXY(INVALID_DIR);
00350 
00351       u->SetNext(w);
00352       VehicleUpdatePosition(w);
00353     }
00354   }
00355 
00356   return CommandCost();
00357 }
00358 
00359 
00360 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00361 {
00362   const Station *st = GetTargetAirportIfValid(this);
00363   /* If the station is not a valid airport or if it has no hangars */
00364   if (st == NULL || !st->airport.HasHangar()) {
00365     /* the aircraft has to search for a hangar on its own */
00366     StationID station = FindNearestHangar(this);
00367 
00368     if (station == INVALID_STATION) return false;
00369 
00370     st = Station::Get(station);
00371   }
00372 
00373   if (location    != NULL) *location    = st->xy;
00374   if (destination != NULL) *destination = st->index;
00375 
00376   return true;
00377 }
00378 
00379 static void CheckIfAircraftNeedsService(Aircraft *v)
00380 {
00381   if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00382   if (v->IsChainInDepot()) {
00383     VehicleServiceInDepot(v);
00384     return;
00385   }
00386 
00387   /* When we're parsing conditional orders and the like
00388    * we don't want to consider going to a depot too. */
00389   if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
00390 
00391   const Station *st = Station::Get(v->current_order.GetDestination());
00392 
00393   assert(st != NULL);
00394 
00395   /* only goto depot if the target airport has a depot */
00396   if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
00397     v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00398     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00399   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00400     v->current_order.MakeDummy();
00401     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00402   }
00403 }
00404 
00405 Money Aircraft::GetRunningCost() const
00406 {
00407   const Engine *e = this->GetEngine();
00408   uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00409   return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
00410 }
00411 
00412 void Aircraft::OnNewDay()
00413 {
00414   if (!this->IsNormalAircraft()) return;
00415 
00416   if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00417 
00418   CheckOrders(this);
00419 
00420   CheckVehicleBreakdown(this);
00421   AgeVehicle(this);
00422   CheckIfAircraftNeedsService(this);
00423 
00424   if (this->running_ticks == 0) return;
00425 
00426   CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00427 
00428   this->profit_this_year -= cost.GetCost();
00429   this->running_ticks = 0;
00430 
00431   SubtractMoneyFromCompanyFract(this->owner, cost);
00432 
00433   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00434   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00435 }
00436 
00437 static void HelicopterTickHandler(Aircraft *v)
00438 {
00439   Aircraft *u = v->Next()->Next();
00440 
00441   if (u->vehstatus & VS_HIDDEN) return;
00442 
00443   /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
00444    * loading/unloading at a terminal or stopped */
00445   if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00446     if (u->cur_speed != 0) {
00447       u->cur_speed++;
00448       if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00449         u->cur_speed = 0;
00450       }
00451     }
00452   } else {
00453     if (u->cur_speed == 0) {
00454       u->cur_speed = 0x70;
00455     }
00456     if (u->cur_speed >= 0x50) {
00457       u->cur_speed--;
00458     }
00459   }
00460 
00461   int tick = ++u->tick_counter;
00462   int spd = u->cur_speed >> 4;
00463 
00464   SpriteID img;
00465   if (spd == 0) {
00466     u->state = HRS_ROTOR_STOPPED;
00467     img = GetRotorImage(v, EIT_ON_MAP);
00468     if (u->cur_image == img) return;
00469   } else if (tick >= spd) {
00470     u->tick_counter = 0;
00471     u->state++;
00472     if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00473     img = GetRotorImage(v, EIT_ON_MAP);
00474   } else {
00475     return;
00476   }
00477 
00478   u->cur_image = img;
00479 
00480   VehicleUpdatePositionAndViewport(u);
00481 }
00482 
00490 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00491 {
00492   v->x_pos = x;
00493   v->y_pos = y;
00494   v->z_pos = z;
00495 
00496   VehicleUpdatePosition(v);
00497   v->UpdateViewport(true, false);
00498   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
00499 
00500   Aircraft *u = v->Next();
00501 
00502   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00503   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00504   u->x_pos = x;
00505   u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
00506 
00507   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00508   u->z_pos = GetSlopePixelZ(safe_x, safe_y);
00509   u->cur_image = v->cur_image;
00510 
00511   VehicleUpdatePositionAndViewport(u);
00512 
00513   u = u->Next();
00514   if (u != NULL) {
00515     u->x_pos = x;
00516     u->y_pos = y;
00517     u->z_pos = z + ROTOR_Z_OFFSET;
00518 
00519     VehicleUpdatePositionAndViewport(u);
00520   }
00521 }
00522 
00527 void HandleAircraftEnterHangar(Aircraft *v)
00528 {
00529   v->subspeed = 0;
00530   v->progress = 0;
00531 
00532   Aircraft *u = v->Next();
00533   u->vehstatus |= VS_HIDDEN;
00534   u = u->Next();
00535   if (u != NULL) {
00536     u->vehstatus |= VS_HIDDEN;
00537     u->cur_speed = 0;
00538   }
00539 
00540   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00541 }
00542 
00543 static void PlayAircraftSound(const Vehicle *v)
00544 {
00545   if (!PlayVehicleSound(v, VSE_START)) {
00546     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00547   }
00548 }
00549 
00550 
00557 void UpdateAircraftCache(Aircraft *v, bool update_range)
00558 {
00559   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00560   if (max_speed != 0) {
00561     /* Convert from original units to km-ish/h */
00562     max_speed = (max_speed * 128) / 10;
00563 
00564     v->vcache.cached_max_speed = max_speed;
00565   } else {
00566     /* Use the default max speed of the vehicle. */
00567     v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00568   }
00569 
00570   /* Update cargo aging period. */
00571   v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00572   Aircraft *u = v->Next(); // Shadow for mail
00573   u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00574 
00575   /* Update aircraft range. */
00576   if (update_range) {
00577     v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
00578     /* Squared it now so we don't have to do it later all the time. */
00579     v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
00580   }
00581 }
00582 
00583 
00587 enum AircraftSpeedLimits {
00588   SPEED_LIMIT_TAXI     =     50,  
00589   SPEED_LIMIT_APPROACH =    230,  
00590   SPEED_LIMIT_BROKEN   =    320,  
00591   SPEED_LIMIT_HOLD     =    425,  
00592   SPEED_LIMIT_NONE     = 0xFFFF,  
00593 };
00594 
00602 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00603 {
00611   uint spd = v->acceleration * 77;
00612   byte t;
00613 
00614   /* Adjust speed limits by plane speed factor to prevent taxiing
00615    * and take-off speeds being too low. */
00616   speed_limit *= _settings_game.vehicle.plane_speed;
00617 
00618   if (v->vcache.cached_max_speed < speed_limit) {
00619     if (v->cur_speed < speed_limit) hard_limit = false;
00620     speed_limit = v->vcache.cached_max_speed;
00621   }
00622 
00623   v->subspeed = (t = v->subspeed) + (byte)spd;
00624 
00625   /* Aircraft's current speed is used twice so that very fast planes are
00626    * forced to slow down rapidly in the short distance needed. The magic
00627    * value 16384 was determined to give similar results to the old speed/48
00628    * method at slower speeds. This also results in less reduction at slow
00629    * speeds to that aircraft do not get to taxi speed straight after
00630    * touchdown. */
00631   if (!hard_limit && v->cur_speed > speed_limit) {
00632     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00633   }
00634 
00635   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00636 
00637   /* adjust speed for broken vehicles */
00638   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00639 
00640   /* updates statusbar only if speed have changed to save CPU time */
00641   if (spd != v->cur_speed) {
00642     v->cur_speed = spd;
00643     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00644   }
00645 
00646   /* Adjust distance moved by plane speed setting */
00647   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00648 
00649   /* Convert direction-independent speed into direction-dependent speed. (old movement method) */
00650   spd = v->GetOldAdvanceSpeed(spd);
00651 
00652   spd += v->progress;
00653   v->progress = (byte)spd;
00654   return spd >> 8;
00655 }
00656 
00664 int GetAircraftFlyingAltitude(const Aircraft *v)
00665 {
00666   if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00667 
00668   /* Make sure Aircraft fly no lower so that they don't conduct
00669    * CFITs (controlled flight into terrain)
00670    */
00671   int base_altitude = PLANE_HOLDING_ALTITUDE;
00672 
00673   /* Make sure eastbound and westbound planes do not "crash" into each
00674    * other by providing them with vertical separation
00675    */
00676   switch (v->direction) {
00677     case DIR_N:
00678     case DIR_NE:
00679     case DIR_E:
00680     case DIR_SE:
00681       base_altitude += 10;
00682       break;
00683 
00684     default: break;
00685   }
00686 
00687   /* Make faster planes fly higher so that they can overtake slower ones */
00688   base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00689 
00690   return base_altitude;
00691 }
00692 
00707 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00708 {
00709   assert(v != NULL);
00710   assert(apc != NULL);
00711 
00712   /* In the case the station doesn't exit anymore, set target tile 0.
00713    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00714    * or it will simply crash in next tick */
00715   TileIndex tile = 0;
00716 
00717   const Station *st = Station::GetIfValid(v->targetairport);
00718   if (st != NULL) {
00719     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00720     tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00721   }
00722 
00723   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00724   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00725 
00726   DiagDirection dir;
00727   if (abs(delta_y) < abs(delta_x)) {
00728     /* We are northeast or southwest of the airport */
00729     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00730   } else {
00731     /* We are northwest or southeast of the airport */
00732     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00733   }
00734   dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00735   return apc->entry_points[dir];
00736 }
00737 
00738 
00739 static void MaybeCrashAirplane(Aircraft *v);
00740 
00748 static bool AircraftController(Aircraft *v)
00749 {
00750   int count;
00751 
00752   /* NULL if station is invalid */
00753   const Station *st = Station::GetIfValid(v->targetairport);
00754   /* INVALID_TILE if there is no station */
00755   TileIndex tile = INVALID_TILE;
00756   Direction rotation = DIR_N;
00757   uint size_x = 1, size_y = 1;
00758   if (st != NULL) {
00759     if (st->airport.tile != INVALID_TILE) {
00760       tile = st->airport.tile;
00761       rotation = st->airport.rotation;
00762       size_x = st->airport.w;
00763       size_y = st->airport.h;
00764     } else {
00765       tile = st->xy;
00766     }
00767   }
00768   /* DUMMY if there is no station or no airport */
00769   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00770 
00771   /* prevent going to INVALID_TILE if airport is deleted. */
00772   if (st == NULL || st->airport.tile == INVALID_TILE) {
00773     /* Jump into our "holding pattern" state machine if possible */
00774     if (v->pos >= afc->nofelements) {
00775       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00776     } else if (v->targetairport != v->current_order.GetDestination()) {
00777       /* If not possible, just get out of here fast */
00778       v->state = FLYING;
00779       UpdateAircraftCache(v);
00780       AircraftNextAirportPos_and_Order(v);
00781       /* get aircraft back on running altitude */
00782       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00783       return false;
00784     }
00785   }
00786 
00787   /*  get airport moving data */
00788   const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00789 
00790   int x = TileX(tile) * TILE_SIZE;
00791   int y = TileY(tile) * TILE_SIZE;
00792 
00793   /* Helicopter raise */
00794   if (amd.flag & AMED_HELI_RAISE) {
00795     Aircraft *u = v->Next()->Next();
00796 
00797     /* Make sure the rotors don't rotate too fast */
00798     if (u->cur_speed > 32) {
00799       v->cur_speed = 0;
00800       if (--u->cur_speed == 32) {
00801         if (!PlayVehicleSound(v, VSE_START)) {
00802           SndPlayVehicleFx(SND_18_HELICOPTER, v);
00803         }
00804       }
00805     } else {
00806       u->cur_speed = 32;
00807       count = UpdateAircraftSpeed(v);
00808       if (count > 0) {
00809         v->tile = 0;
00810         int z_dest = GetAircraftFlyingAltitude(v);
00811 
00812         /* Reached altitude? */
00813         if (v->z_pos >= z_dest) {
00814           v->cur_speed = 0;
00815           return true;
00816         }
00817         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00818       }
00819     }
00820     return false;
00821   }
00822 
00823   /* Helicopter landing. */
00824   if (amd.flag & AMED_HELI_LOWER) {
00825     if (st == NULL) {
00826       /* FIXME - AircraftController -> if station no longer exists, do not land
00827        * helicopter will circle until sign disappears, then go to next order
00828        * what to do when it is the only order left, right now it just stays in 1 place */
00829       v->state = FLYING;
00830       UpdateAircraftCache(v);
00831       AircraftNextAirportPos_and_Order(v);
00832       return false;
00833     }
00834 
00835     /* Vehicle is now at the airport. */
00836     v->tile = tile;
00837 
00838     /* Find altitude of landing position. */
00839     int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00840 
00841     if (z == v->z_pos) {
00842       Vehicle *u = v->Next()->Next();
00843 
00844       /*  Increase speed of rotors. When speed is 80, we've landed. */
00845       if (u->cur_speed >= 80) return true;
00846       u->cur_speed += 4;
00847     } else {
00848       count = UpdateAircraftSpeed(v);
00849       if (count > 0) {
00850         if (v->z_pos > z) {
00851           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00852         } else {
00853           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00854         }
00855       }
00856     }
00857     return false;
00858   }
00859 
00860   /* Get distance from destination pos to current pos. */
00861   uint dist = abs(x + amd.x - v->x_pos) +  abs(y + amd.y - v->y_pos);
00862 
00863   /* Need exact position? */
00864   if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00865 
00866   /* At final pos? */
00867   if (dist == 0) {
00868     /* Change direction smoothly to final direction. */
00869     DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00870     /* if distance is 0, and plane points in right direction, no point in calling
00871      * UpdateAircraftSpeed(). So do it only afterwards */
00872     if (dirdiff == DIRDIFF_SAME) {
00873       v->cur_speed = 0;
00874       return true;
00875     }
00876 
00877     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00878 
00879     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00880     v->cur_speed >>= 1;
00881 
00882     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00883     return false;
00884   }
00885 
00886   if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00887     MaybeCrashAirplane(v);
00888     if ((v->vehstatus & VS_CRASHED) != 0) return false;
00889   }
00890 
00891   uint speed_limit = SPEED_LIMIT_TAXI;
00892   bool hard_limit = true;
00893 
00894   if (amd.flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
00895   if (amd.flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
00896   if (amd.flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00897   if (amd.flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
00898 
00899   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00900   if (count == 0) return false;
00901 
00902   if (v->turn_counter != 0) v->turn_counter--;
00903 
00904   do {
00905 
00906     GetNewVehiclePosResult gp;
00907 
00908     if (dist < 4 || (amd.flag & AMED_LAND)) {
00909       /* move vehicle one pixel towards target */
00910       gp.x = (v->x_pos != (x + amd.x)) ?
00911           v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00912           v->x_pos;
00913       gp.y = (v->y_pos != (y + amd.y)) ?
00914           v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00915           v->y_pos;
00916 
00917       /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
00918       gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00919 
00920     } else {
00921 
00922       /* Turn. Do it slowly if in the air. */
00923       Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00924       if (newdir != v->direction) {
00925         if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00926           if (v->turn_counter == 0 || newdir == v->last_direction) {
00927             if (newdir == v->last_direction) {
00928               v->number_consecutive_turns = 0;
00929             } else {
00930               v->number_consecutive_turns++;
00931             }
00932             v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00933             v->last_direction = v->direction;
00934             v->direction = newdir;
00935           }
00936 
00937           /* Move vehicle. */
00938           gp = GetNewVehiclePos(v);
00939         } else {
00940           v->cur_speed >>= 1;
00941           v->direction = newdir;
00942 
00943           /* When leaving a terminal an aircraft often goes to a position
00944            * directly in front of it. If it would move while turning it
00945            * would need an two extra turns to end up at the correct position.
00946            * To make it easier just disallow all moving while turning as
00947            * long as an aircraft is on the ground. */
00948           gp.x = v->x_pos;
00949           gp.y = v->y_pos;
00950           gp.new_tile = gp.old_tile = v->tile;
00951         }
00952       } else {
00953         v->number_consecutive_turns = 0;
00954         /* Move vehicle. */
00955         gp = GetNewVehiclePos(v);
00956       }
00957     }
00958 
00959     v->tile = gp.new_tile;
00960     /* If vehicle is in the air, use tile coordinate 0. */
00961     if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00962 
00963     /* Adjust Z for land or takeoff? */
00964     int z = v->z_pos;
00965 
00966     if (amd.flag & AMED_TAKEOFF) {
00967       z = min(z + 2, GetAircraftFlyingAltitude(v));
00968     }
00969 
00970     /* Let the plane drop from normal flight altitude to holding pattern altitude */
00971     if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00972 
00973     if (amd.flag & AMED_LAND) {
00974       if (st->airport.tile == INVALID_TILE) {
00975         /* Airport has been removed, abort the landing procedure */
00976         v->state = FLYING;
00977         UpdateAircraftCache(v);
00978         AircraftNextAirportPos_and_Order(v);
00979         /* get aircraft back on running altitude */
00980         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00981         continue;
00982       }
00983 
00984       int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00985 
00986       /* We're not flying below our destination, right? */
00987       assert(curz <= z);
00988       int t = max(1U, dist - 4);
00989       int delta = z - curz;
00990 
00991       /* Only start lowering when we're sufficiently close for a 1:1 glide */
00992       if (delta >= t) {
00993         z -= CeilDiv(z - curz, t);
00994       }
00995       if (z < curz) z = curz;
00996     }
00997 
00998     /* We've landed. Decrease speed when we're reaching end of runway. */
00999     if (amd.flag & AMED_BRAKE) {
01000       int curz = GetSlopePixelZ(x, y) + 1;
01001 
01002       if (z > curz) {
01003         z--;
01004       } else if (z < curz) {
01005         z++;
01006       }
01007 
01008     }
01009 
01010     SetAircraftPosition(v, gp.x, gp.y, z);
01011   } while (--count != 0);
01012   return false;
01013 }
01014 
01019 static bool HandleCrashedAircraft(Aircraft *v)
01020 {
01021   v->crashed_counter += 3;
01022 
01023   Station *st = GetTargetAirportIfValid(v);
01024 
01025   /* make aircraft crash down to the ground */
01026   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01027     int z = GetSlopePixelZ(v->x_pos, v->y_pos);
01028     v->z_pos -= 1;
01029     if (v->z_pos == z) {
01030       v->crashed_counter = 500;
01031       v->z_pos++;
01032     }
01033   }
01034 
01035   if (v->crashed_counter < 650) {
01036     uint32 r;
01037     if (Chance16R(1, 32, r)) {
01038       static const DirDiff delta[] = {
01039         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01040       };
01041 
01042       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01043       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01044       r = Random();
01045       CreateEffectVehicleRel(v,
01046         GB(r, 0, 4) - 4,
01047         GB(r, 4, 4) - 4,
01048         GB(r, 8, 4),
01049         EV_EXPLOSION_SMALL);
01050     }
01051   } else if (v->crashed_counter >= 10000) {
01052     /*  remove rubble of crashed airplane */
01053 
01054     /* clear runway-in on all airports, set by crashing plane
01055      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01056      * but they all share the same number */
01057     if (st != NULL) {
01058       CLRBITS(st->airport.flags, RUNWAY_IN_block);
01059       CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
01060       CLRBITS(st->airport.flags, RUNWAY_IN2_block);    // intercontinental
01061     }
01062 
01063     delete v;
01064 
01065     return false;
01066   }
01067 
01068   return true;
01069 }
01070 
01071 
01077 static void HandleAircraftSmoke(Aircraft *v, bool mode)
01078 {
01079   static const struct {
01080     int8 x;
01081     int8 y;
01082   } smoke_pos[] = {
01083     {  5,  5 },
01084     {  6,  0 },
01085     {  5, -5 },
01086     {  0, -6 },
01087     { -5, -5 },
01088     { -6,  0 },
01089     { -5,  5 },
01090     {  0,  6 }
01091   };
01092 
01093   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01094 
01095   /* Stop smoking when landed */
01096   if (v->cur_speed < 10) {
01097     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01098     v->breakdown_ctr = 0;
01099     return;
01100   }
01101 
01102   /* Spawn effect et most once per Tick, i.e. !mode */
01103   if (!mode && (v->tick_counter & 0x0F) == 0) {
01104     CreateEffectVehicleRel(v,
01105       smoke_pos[v->direction].x,
01106       smoke_pos[v->direction].y,
01107       2,
01108       EV_BREAKDOWN_SMOKE_AIRCRAFT
01109     );
01110   }
01111 }
01112 
01113 void HandleMissingAircraftOrders(Aircraft *v)
01114 {
01115   /*
01116    * We do not have an order. This can be divided into two cases:
01117    * 1) we are heading to an invalid station. In this case we must
01118    *    find another airport to go to. If there is nowhere to go,
01119    *    we will destroy the aircraft as it otherwise will enter
01120    *    the holding pattern for the first airport, which can cause
01121    *    the plane to go into an undefined state when building an
01122    *    airport with the same StationID.
01123    * 2) we are (still) heading to a (still) valid airport, then we
01124    *    can continue going there. This can happen when you are
01125    *    changing the aircraft's orders while in-flight or in for
01126    *    example a depot. However, when we have a current order to
01127    *    go to a depot, we have to keep that order so the aircraft
01128    *    actually stops.
01129    */
01130   const Station *st = GetTargetAirportIfValid(v);
01131   if (st == NULL) {
01132     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01133     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01134     cur_company.Restore();
01135 
01136     if (ret.Failed()) CrashAirplane(v);
01137   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01138     v->current_order.Free();
01139   }
01140 }
01141 
01142 
01143 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01144 {
01145   /* Orders are changed in flight, ensure going to the right station. */
01146   if (this->state == FLYING) {
01147     AircraftNextAirportPos_and_Order(this);
01148   }
01149 
01150   /* Aircraft do not use dest-tile */
01151   return 0;
01152 }
01153 
01154 void Aircraft::MarkDirty()
01155 {
01156   this->UpdateViewport(false, false);
01157   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01158 }
01159 
01160 
01161 uint Aircraft::Crash(bool flooded)
01162 {
01163   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01164   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01165 
01166   return pass;
01167 }
01168 
01173 static void CrashAirplane(Aircraft *v)
01174 {
01175   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01176 
01177   uint pass = v->Crash();
01178   SetDParam(0, pass);
01179 
01180   v->cargo.Truncate();
01181   v->Next()->cargo.Truncate();
01182   const Station *st = GetTargetAirportIfValid(v);
01183   StringID newsitem;
01184   if (st == NULL) {
01185     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01186   } else {
01187     SetDParam(1, st->index);
01188     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01189   }
01190 
01191   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01192   Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01193 
01194   AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
01195 
01196   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01197   if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
01198 }
01199 
01204 static void MaybeCrashAirplane(Aircraft *v)
01205 {
01206   if (_settings_game.vehicle.plane_crashes == 0) return;
01207 
01208   Station *st = Station::Get(v->targetairport);
01209 
01210   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01211   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01212   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01213       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01214       !_cheats.no_jetcrash.value) {
01215     prob /= 20;
01216   } else {
01217     prob /= 1500;
01218   }
01219 
01220   if (GB(Random(), 0, 22) > prob) return;
01221 
01222   /* Crash the airplane. Remove all goods stored at the station. */
01223   for (CargoID i = 0; i < NUM_CARGO; i++) {
01224     st->goods[i].rating = 1;
01225     st->goods[i].cargo.Truncate();
01226   }
01227 
01228   CrashAirplane(v);
01229 }
01230 
01236 static void AircraftEntersTerminal(Aircraft *v)
01237 {
01238   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01239 
01240   Station *st = Station::Get(v->targetairport);
01241   v->last_station_visited = v->targetairport;
01242 
01243   /* Check if station was ever visited before */
01244   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01245     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01246     SetDParam(0, st->index);
01247     /* show newsitem of celebrating citizens */
01248     AddVehicleNewsItem(
01249       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01250       (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
01251       v->index,
01252       st->index
01253     );
01254     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01255     Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
01256   }
01257 
01258   v->BeginLoading();
01259 }
01260 
01265 static void AircraftLandAirplane(Aircraft *v)
01266 {
01267   v->UpdateDeltaXY(INVALID_DIR);
01268 
01269   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01270     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01271   }
01272 }
01273 
01274 
01276 void AircraftNextAirportPos_and_Order(Aircraft *v)
01277 {
01278   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01279     v->targetairport = v->current_order.GetDestination();
01280   }
01281 
01282   const Station *st = GetTargetAirportIfValid(v);
01283   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01284   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01285   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01286 }
01287 
01296 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01297 {
01298   v->cur_speed = 0;
01299   v->subspeed = 0;
01300   v->progress = 0;
01301   v->direction = exit_dir;
01302   v->vehstatus &= ~VS_HIDDEN;
01303   {
01304     Vehicle *u = v->Next();
01305     u->vehstatus &= ~VS_HIDDEN;
01306 
01307     /* Rotor blades */
01308     u = u->Next();
01309     if (u != NULL) {
01310       u->vehstatus &= ~VS_HIDDEN;
01311       u->cur_speed = 80;
01312     }
01313   }
01314 
01315   VehicleServiceInDepot(v);
01316   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01317   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01318   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01319 }
01320 
01324 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01325 {
01326   AircraftEntersTerminal(v);
01327   v->state = apc->layout[v->pos].heading;
01328 }
01329 
01335 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01336 {
01337   VehicleEnterDepot(v);
01338   v->state = apc->layout[v->pos].heading;
01339 }
01340 
01346 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01347 {
01348   /* if we just arrived, execute EnterHangar first */
01349   if (v->previous_pos != v->pos) {
01350     AircraftEventHandler_EnterHangar(v, apc);
01351     return;
01352   }
01353 
01354   /* if we were sent to the depot, stay there */
01355   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01356     v->current_order.Free();
01357     return;
01358   }
01359 
01360   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01361       !v->current_order.IsType(OT_GOTO_DEPOT))
01362     return;
01363 
01364   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
01365   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01366     VehicleEnterDepot(v);
01367     return;
01368   }
01369 
01370   /* if the block of the next position is busy, stay put */
01371   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01372 
01373   /* We are already at the target airport, we need to find a terminal */
01374   if (v->current_order.GetDestination() == v->targetairport) {
01375     /* FindFreeTerminal:
01376      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01377     if (v->subtype == AIR_HELICOPTER) {
01378       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01379     } else {
01380       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01381     }
01382   } else { // Else prepare for launch.
01383     /* airplane goto state takeoff, helicopter to helitakeoff */
01384     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01385   }
01386   const Station *st = Station::GetByTile(v->tile);
01387   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01388   AirportMove(v, apc);
01389 }
01390 
01392 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01393 {
01394   /* if we just arrived, execute EnterTerminal first */
01395   if (v->previous_pos != v->pos) {
01396     AircraftEventHandler_EnterTerminal(v, apc);
01397     /* on an airport with helipads, a helicopter will always land there
01398      * and get serviced at the same time - setting */
01399     if (_settings_game.order.serviceathelipad) {
01400       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01401         /* an excerpt of ServiceAircraft, without the invisibility stuff */
01402         v->date_of_last_service = _date;
01403         v->breakdowns_since_last_service = 0;
01404         v->reliability = v->GetEngine()->reliability;
01405         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01406       }
01407     }
01408     return;
01409   }
01410 
01411   if (v->current_order.IsType(OT_NOTHING)) return;
01412 
01413   /* if the block of the next position is busy, stay put */
01414   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01415 
01416   /* airport-road is free. We either have to go to another airport, or to the hangar
01417    * ---> start moving */
01418 
01419   bool go_to_hangar = false;
01420   switch (v->current_order.GetType()) {
01421     case OT_GOTO_STATION: // ready to fly to another airport
01422       break;
01423     case OT_GOTO_DEPOT:   // visit hangar for servicing, sale, etc.
01424       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01425       break;
01426     case OT_CONDITIONAL:
01427       /* In case of a conditional order we just have to wait a tick
01428        * longer, so the conditional order can actually be processed;
01429        * we should not clear the order as that makes us go nowhere. */
01430       return;
01431     default:  // orders have been deleted (no orders), goto depot and don't bother us
01432       v->current_order.Free();
01433       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01434   }
01435 
01436   if (go_to_hangar) {
01437     v->state = HANGAR;
01438   } else {
01439     /* airplane goto state takeoff, helicopter to helitakeoff */
01440     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01441   }
01442   AirportMove(v, apc);
01443 }
01444 
01445 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01446 {
01447   error("OK, you shouldn't be here, check your Airport Scheme!");
01448 }
01449 
01450 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01451 {
01452   PlayAircraftSound(v); // play takeoffsound for airplanes
01453   v->state = STARTTAKEOFF;
01454 }
01455 
01456 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01457 {
01458   v->state = ENDTAKEOFF;
01459   v->UpdateDeltaXY(INVALID_DIR);
01460 }
01461 
01462 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01463 {
01464   v->state = FLYING;
01465   /* get the next position to go to, differs per airport */
01466   AircraftNextAirportPos_and_Order(v);
01467 }
01468 
01469 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01470 {
01471   v->state = FLYING;
01472   v->UpdateDeltaXY(INVALID_DIR);
01473 
01474   /* get the next position to go to, differs per airport */
01475   AircraftNextAirportPos_and_Order(v);
01476 
01477   /* Send the helicopter to a hangar if needed for replacement */
01478   if (v->NeedsAutomaticServicing()) {
01479     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01480     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01481     cur_company.Restore();
01482   }
01483 }
01484 
01485 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01486 {
01487   Station *st = Station::Get(v->targetairport);
01488 
01489   /* Runway busy, not allowed to use this airstation or closed, circle. */
01490   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
01491     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01492      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01493      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01494     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01495     const AirportFTA *current = apc->layout[v->pos].next;
01496     while (current != NULL) {
01497       if (current->heading == landingtype) {
01498         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01499          * we don't want that for plane in air
01500          * hack for speed thingie */
01501         uint16 tcur_speed = v->cur_speed;
01502         uint16 tsubspeed = v->subspeed;
01503         if (!AirportHasBlock(v, current, apc)) {
01504           v->state = landingtype; // LANDING / HELILANDING
01505           /* it's a bit dirty, but I need to set position to next position, otherwise
01506            * if there are multiple runways, plane won't know which one it took (because
01507            * they all have heading LANDING). And also occupy that block! */
01508           v->pos = current->next_position;
01509           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01510           return;
01511         }
01512         v->cur_speed = tcur_speed;
01513         v->subspeed = tsubspeed;
01514       }
01515       current = current->next;
01516     }
01517   }
01518   v->state = FLYING;
01519   v->pos = apc->layout[v->pos].next_position;
01520 }
01521 
01522 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01523 {
01524   v->state = ENDLANDING;
01525   AircraftLandAirplane(v);  // maybe crash airplane
01526 
01527   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01528   if (v->NeedsAutomaticServicing()) {
01529     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01530     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01531     cur_company.Restore();
01532   }
01533 }
01534 
01535 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01536 {
01537   v->state = HELIENDLANDING;
01538   v->UpdateDeltaXY(INVALID_DIR);
01539 }
01540 
01541 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01542 {
01543   /* next block busy, don't do a thing, just wait */
01544   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01545 
01546   /* if going to terminal (OT_GOTO_STATION) choose one
01547    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01548    * 2. not going for terminal (but depot, no order),
01549    * --> get out of the way to the hangar. */
01550   if (v->current_order.IsType(OT_GOTO_STATION)) {
01551     if (AirportFindFreeTerminal(v, apc)) return;
01552   }
01553   v->state = HANGAR;
01554 
01555 }
01556 
01557 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01558 {
01559   /*  next block busy, don't do a thing, just wait */
01560   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01561 
01562   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01563    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01564    * 2. not going for terminal (but depot, no order),
01565    * --> get out of the way to the hangar IF there are terminals on the airport.
01566    * --> else TAKEOFF
01567    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01568    * must go to a hangar. */
01569   if (v->current_order.IsType(OT_GOTO_STATION)) {
01570     if (AirportFindFreeHelipad(v, apc)) return;
01571   }
01572   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01573 }
01574 
01580 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01582 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01583   AircraftEventHandler_General,        // TO_ALL         =  0
01584   AircraftEventHandler_InHangar,       // HANGAR         =  1
01585   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01586   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01587   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01588   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01589   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01590   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01591   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01592   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01593   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01594   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01595   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01596   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01597   AircraftEventHandler_Flying,         // FLYING         = 14
01598   AircraftEventHandler_Landing,        // LANDING        = 15
01599   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01600   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01601   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01602   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01603   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01604   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01605 };
01606 
01607 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01608 {
01609   /* we have left the previous block, and entered the new one. Free the previous block */
01610   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01611     Station *st = Station::Get(v->targetairport);
01612 
01613     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01614   }
01615 }
01616 
01617 static void AirportGoToNextPosition(Aircraft *v)
01618 {
01619   /* if aircraft is not in position, wait until it is */
01620   if (!AircraftController(v)) return;
01621 
01622   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01623 
01624   AirportClearBlock(v, apc);
01625   AirportMove(v, apc); // move aircraft to next position
01626 }
01627 
01628 /* gets pos from vehicle and next orders */
01629 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01630 {
01631   /* error handling */
01632   if (v->pos >= apc->nofelements) {
01633     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01634     assert(v->pos < apc->nofelements);
01635   }
01636 
01637   const AirportFTA *current = &apc->layout[v->pos];
01638   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01639   if (current->heading == v->state) {
01640     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01641     byte prev_state = v->state;
01642     _aircraft_state_handlers[v->state](v, apc);
01643     if (v->state != FLYING) v->previous_pos = prev_pos;
01644     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01645     return true;
01646   }
01647 
01648   v->previous_pos = v->pos; // save previous location
01649 
01650   /* there is only one choice to move to */
01651   if (current->next == NULL) {
01652     if (AirportSetBlocks(v, current, apc)) {
01653       v->pos = current->next_position;
01654       UpdateAircraftCache(v);
01655     } // move to next position
01656     return false;
01657   }
01658 
01659   /* there are more choices to choose from, choose the one that
01660    * matches our heading */
01661   do {
01662     if (v->state == current->heading || current->heading == TO_ALL) {
01663       if (AirportSetBlocks(v, current, apc)) {
01664         v->pos = current->next_position;
01665         UpdateAircraftCache(v);
01666       } // move to next position
01667       return false;
01668     }
01669     current = current->next;
01670   } while (current != NULL);
01671 
01672   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01673   NOT_REACHED();
01674 }
01675 
01677 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01678 {
01679   const AirportFTA *reference = &apc->layout[v->pos];
01680   const AirportFTA *next = &apc->layout[current_pos->next_position];
01681 
01682   /* same block, then of course we can move */
01683   if (apc->layout[current_pos->position].block != next->block) {
01684     const Station *st = Station::Get(v->targetairport);
01685     uint64 airport_flags = next->block;
01686 
01687     /* check additional possible extra blocks */
01688     if (current_pos != reference && current_pos->block != NOTHING_block) {
01689       airport_flags |= current_pos->block;
01690     }
01691 
01692     if (st->airport.flags & airport_flags) {
01693       v->cur_speed = 0;
01694       v->subspeed = 0;
01695       return true;
01696     }
01697   }
01698   return false;
01699 }
01700 
01708 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01709 {
01710   const AirportFTA *next = &apc->layout[current_pos->next_position];
01711   const AirportFTA *reference = &apc->layout[v->pos];
01712 
01713   /* if the next position is in another block, check it and wait until it is free */
01714   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01715     uint64 airport_flags = next->block;
01716     /* search for all all elements in the list with the same state, and blocks != N
01717      * this means more blocks should be checked/set */
01718     const AirportFTA *current = current_pos;
01719     if (current == reference) current = current->next;
01720     while (current != NULL) {
01721       if (current->heading == current_pos->heading && current->block != 0) {
01722         airport_flags |= current->block;
01723         break;
01724       }
01725       current = current->next;
01726     }
01727 
01728     /* if the block to be checked is in the next position, then exclude that from
01729      * checking, because it has been set by the airplane before */
01730     if (current_pos->block == next->block) airport_flags ^= next->block;
01731 
01732     Station *st = Station::Get(v->targetairport);
01733     if (st->airport.flags & airport_flags) {
01734       v->cur_speed = 0;
01735       v->subspeed = 0;
01736       return false;
01737     }
01738 
01739     if (next->block != NOTHING_block) {
01740       SETBITS(st->airport.flags, airport_flags); // occupy next block
01741     }
01742   }
01743   return true;
01744 }
01745 
01750 struct MovementTerminalMapping {
01751   AirportMovementStates state; 
01752   uint64 airport_flag;         
01753 };
01754 
01756 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01757   {TERM1, TERM1_block},
01758   {TERM2, TERM2_block},
01759   {TERM3, TERM3_block},
01760   {TERM4, TERM4_block},
01761   {TERM5, TERM5_block},
01762   {TERM6, TERM6_block},
01763   {TERM7, TERM7_block},
01764   {TERM8, TERM8_block},
01765   {HELIPAD1, HELIPAD1_block},
01766   {HELIPAD2, HELIPAD2_block},
01767   {HELIPAD3, HELIPAD3_block},
01768 };
01769 
01777 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01778 {
01779   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01780   Station *st = Station::Get(v->targetairport);
01781   for (; i < last_terminal; i++) {
01782     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01783       /* TERMINAL# HELIPAD# */
01784       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01785       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01786       return true;
01787     }
01788   }
01789   return false;
01790 }
01791 
01797 static uint GetNumTerminals(const AirportFTAClass *apc)
01798 {
01799   uint num = 0;
01800 
01801   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01802 
01803   return num;
01804 }
01805 
01812 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01813 {
01814   /* example of more terminalgroups
01815    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01816    * Heading 255 denotes a group. We see 2 groups here:
01817    * 1. group 0 -- TERM_GROUP1_block (check block)
01818    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01819    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01820    * looks at the corresponding terminals of that group. If no free ones are found, other
01821    * possible groups are checked (in this case group 1, since that is after group 0). If that
01822    * fails, then attempt fails and plane waits
01823    */
01824   if (apc->terminals[0] > 1) {
01825     const Station *st = Station::Get(v->targetairport);
01826     const AirportFTA *temp = apc->layout[v->pos].next;
01827 
01828     while (temp != NULL) {
01829       if (temp->heading == 255) {
01830         if (!(st->airport.flags & temp->block)) {
01831           /* read which group do we want to go to?
01832            * (the first free group) */
01833           uint target_group = temp->next_position + 1;
01834 
01835           /* at what terminal does the group start?
01836            * that means, sum up all terminals of
01837            * groups with lower number */
01838           uint group_start = 0;
01839           for (uint i = 1; i < target_group; i++) {
01840             group_start += apc->terminals[i];
01841           }
01842 
01843           uint group_end = group_start + apc->terminals[target_group];
01844           if (FreeTerminal(v, group_start, group_end)) return true;
01845         }
01846       } else {
01847         /* once the heading isn't 255, we've exhausted the possible blocks.
01848          * So we cannot move */
01849         return false;
01850       }
01851       temp = temp->next;
01852     }
01853   }
01854 
01855   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01856   return FreeTerminal(v, 0, GetNumTerminals(apc));
01857 }
01858 
01865 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01866 {
01867   /* if an airport doesn't have helipads, use terminals */
01868   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01869 
01870   /* only 1 helicoptergroup, check all helipads
01871    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01872   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01873 }
01874 
01880 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01881 {
01882   if (too_far) {
01883     if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01884       SetBit(v->flags, VAF_DEST_TOO_FAR);
01885       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01886       AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01887       if (v->owner == _local_company) {
01888         /* Post a news message. */
01889         SetDParam(0, v->index);
01890         AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
01891       }
01892     }
01893     return;
01894   }
01895 
01896   if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01897     /* Not too far anymore, clear flag and message. */
01898     ClrBit(v->flags, VAF_DEST_TOO_FAR);
01899     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01900     DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01901   }
01902 }
01903 
01904 static bool AircraftEventHandler(Aircraft *v, int loop)
01905 {
01906   if (v->vehstatus & VS_CRASHED) {
01907     return HandleCrashedAircraft(v);
01908   }
01909 
01910   if (v->vehstatus & VS_STOPPED) return true;
01911 
01912   v->HandleBreakdown();
01913 
01914   HandleAircraftSmoke(v, loop != 0);
01915   ProcessOrders(v);
01916   v->HandleLoading(loop != 0);
01917 
01918   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01919 
01920   if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
01921     /* If we are flying, unconditionally clear the 'dest too far' state. */
01922     AircraftHandleDestTooFar(v, false);
01923   } else if (v->acache.cached_max_range_sqr != 0) {
01924     /* Check the distance to the next destination. This code works because the target
01925      * airport is only updated after take off and not on the ground. */
01926     Station *cur_st = Station::GetIfValid(v->targetairport);
01927     Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01928 
01929     if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01930       uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01931       AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01932     }
01933   }
01934 
01935   if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01936 
01937   return true;
01938 }
01939 
01940 bool Aircraft::Tick()
01941 {
01942   if (!this->IsNormalAircraft()) return true;
01943 
01944   this->tick_counter++;
01945 
01946   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01947 
01948   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01949 
01950   this->current_order_time++;
01951 
01952   for (uint i = 0; i != 2; i++) {
01953     /* stop if the aircraft was deleted */
01954     if (!AircraftEventHandler(this, i)) return false;
01955   }
01956 
01957   return true;
01958 }
01959 
01960 
01967 Station *GetTargetAirportIfValid(const Aircraft *v)
01968 {
01969   assert(v->type == VEH_AIRCRAFT);
01970 
01971   Station *st = Station::GetIfValid(v->targetairport);
01972   if (st == NULL) return NULL;
01973 
01974   return st->airport.tile == INVALID_TILE ? NULL : st;
01975 }
01976 
01981 void UpdateAirplanesOnNewStation(const Station *st)
01982 {
01983   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01984   const AirportFTAClass *ap = st->airport.GetFTA();
01985   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01986 
01987   Aircraft *v;
01988   FOR_ALL_AIRCRAFT(v) {
01989     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01990     assert(v->state == FLYING);
01991     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01992     UpdateAircraftCache(v);
01993   }
01994 }