aircraft_cmd.cpp

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