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 
01072 static void HandleAircraftSmoke(Aircraft *v)
01073 {
01074   static const struct {
01075     int8 x;
01076     int8 y;
01077   } smoke_pos[] = {
01078     {  5,  5 },
01079     {  6,  0 },
01080     {  5, -5 },
01081     {  0, -6 },
01082     { -5, -5 },
01083     { -6,  0 },
01084     { -5,  5 },
01085     {  0,  6 }
01086   };
01087 
01088   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01089 
01090   if (v->cur_speed < 10) {
01091     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01092     v->breakdown_ctr = 0;
01093     return;
01094   }
01095 
01096   if ((v->tick_counter & 0x1F) == 0) {
01097     CreateEffectVehicleRel(v,
01098       smoke_pos[v->direction].x,
01099       smoke_pos[v->direction].y,
01100       2,
01101       EV_BREAKDOWN_SMOKE_AIRCRAFT
01102     );
01103   }
01104 }
01105 
01106 void HandleMissingAircraftOrders(Aircraft *v)
01107 {
01108   /*
01109    * We do not have an order. This can be divided into two cases:
01110    * 1) we are heading to an invalid station. In this case we must
01111    *    find another airport to go to. If there is nowhere to go,
01112    *    we will destroy the aircraft as it otherwise will enter
01113    *    the holding pattern for the first airport, which can cause
01114    *    the plane to go into an undefined state when building an
01115    *    airport with the same StationID.
01116    * 2) we are (still) heading to a (still) valid airport, then we
01117    *    can continue going there. This can happen when you are
01118    *    changing the aircraft's orders while in-flight or in for
01119    *    example a depot. However, when we have a current order to
01120    *    go to a depot, we have to keep that order so the aircraft
01121    *    actually stops.
01122    */
01123   const Station *st = GetTargetAirportIfValid(v);
01124   if (st == NULL) {
01125     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01126     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01127     cur_company.Restore();
01128 
01129     if (ret.Failed()) CrashAirplane(v);
01130   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01131     v->current_order.Free();
01132   }
01133 }
01134 
01135 
01136 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01137 {
01138   /* Orders are changed in flight, ensure going to the right station. */
01139   if (this->state == FLYING) {
01140     AircraftNextAirportPos_and_Order(this);
01141   }
01142 
01143   /* Aircraft do not use dest-tile */
01144   return 0;
01145 }
01146 
01147 void Aircraft::MarkDirty()
01148 {
01149   this->UpdateViewport(false, false);
01150   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01151 }
01152 
01153 
01154 uint Aircraft::Crash(bool flooded)
01155 {
01156   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01157   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01158 
01159   return pass;
01160 }
01161 
01166 static void CrashAirplane(Aircraft *v)
01167 {
01168   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01169 
01170   uint pass = v->Crash();
01171   SetDParam(0, pass);
01172 
01173   v->cargo.Truncate();
01174   v->Next()->cargo.Truncate();
01175   const Station *st = GetTargetAirportIfValid(v);
01176   StringID newsitem;
01177   if (st == NULL) {
01178     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01179   } else {
01180     SetDParam(1, st->index);
01181     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01182   }
01183 
01184   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01185   Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01186 
01187   AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
01188 
01189   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01190   if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
01191 }
01192 
01197 static void MaybeCrashAirplane(Aircraft *v)
01198 {
01199   if (_settings_game.vehicle.plane_crashes == 0) return;
01200 
01201   Station *st = Station::Get(v->targetairport);
01202 
01203   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01204   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01205   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01206       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01207       !_cheats.no_jetcrash.value) {
01208     prob /= 20;
01209   } else {
01210     prob /= 1500;
01211   }
01212 
01213   if (GB(Random(), 0, 22) > prob) return;
01214 
01215   /* Crash the airplane. Remove all goods stored at the station. */
01216   for (CargoID i = 0; i < NUM_CARGO; i++) {
01217     st->goods[i].rating = 1;
01218     st->goods[i].cargo.Truncate();
01219   }
01220 
01221   CrashAirplane(v);
01222 }
01223 
01229 static void AircraftEntersTerminal(Aircraft *v)
01230 {
01231   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01232 
01233   Station *st = Station::Get(v->targetairport);
01234   v->last_station_visited = v->targetairport;
01235 
01236   /* Check if station was ever visited before */
01237   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01238     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01239     SetDParam(0, st->index);
01240     /* show newsitem of celebrating citizens */
01241     AddVehicleNewsItem(
01242       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01243       (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
01244       v->index,
01245       st->index
01246     );
01247     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01248     Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
01249   }
01250 
01251   v->BeginLoading();
01252 }
01253 
01258 static void AircraftLandAirplane(Aircraft *v)
01259 {
01260   v->UpdateDeltaXY(INVALID_DIR);
01261 
01262   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01263     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01264   }
01265 }
01266 
01267 
01269 void AircraftNextAirportPos_and_Order(Aircraft *v)
01270 {
01271   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01272     v->targetairport = v->current_order.GetDestination();
01273   }
01274 
01275   const Station *st = GetTargetAirportIfValid(v);
01276   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01277   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01278   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01279 }
01280 
01289 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01290 {
01291   v->cur_speed = 0;
01292   v->subspeed = 0;
01293   v->progress = 0;
01294   v->direction = exit_dir;
01295   v->vehstatus &= ~VS_HIDDEN;
01296   {
01297     Vehicle *u = v->Next();
01298     u->vehstatus &= ~VS_HIDDEN;
01299 
01300     /* Rotor blades */
01301     u = u->Next();
01302     if (u != NULL) {
01303       u->vehstatus &= ~VS_HIDDEN;
01304       u->cur_speed = 80;
01305     }
01306   }
01307 
01308   VehicleServiceInDepot(v);
01309   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01310   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01311   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01312 }
01313 
01317 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01318 {
01319   AircraftEntersTerminal(v);
01320   v->state = apc->layout[v->pos].heading;
01321 }
01322 
01328 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01329 {
01330   VehicleEnterDepot(v);
01331   v->state = apc->layout[v->pos].heading;
01332 }
01333 
01339 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01340 {
01341   /* if we just arrived, execute EnterHangar first */
01342   if (v->previous_pos != v->pos) {
01343     AircraftEventHandler_EnterHangar(v, apc);
01344     return;
01345   }
01346 
01347   /* if we were sent to the depot, stay there */
01348   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01349     v->current_order.Free();
01350     return;
01351   }
01352 
01353   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01354       !v->current_order.IsType(OT_GOTO_DEPOT))
01355     return;
01356 
01357   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
01358   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01359     VehicleEnterDepot(v);
01360     return;
01361   }
01362 
01363   /* if the block of the next position is busy, stay put */
01364   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01365 
01366   /* We are already at the target airport, we need to find a terminal */
01367   if (v->current_order.GetDestination() == v->targetairport) {
01368     /* FindFreeTerminal:
01369      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01370     if (v->subtype == AIR_HELICOPTER) {
01371       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01372     } else {
01373       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01374     }
01375   } else { // Else prepare for launch.
01376     /* airplane goto state takeoff, helicopter to helitakeoff */
01377     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01378   }
01379   const Station *st = Station::GetByTile(v->tile);
01380   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01381   AirportMove(v, apc);
01382 }
01383 
01385 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01386 {
01387   /* if we just arrived, execute EnterTerminal first */
01388   if (v->previous_pos != v->pos) {
01389     AircraftEventHandler_EnterTerminal(v, apc);
01390     /* on an airport with helipads, a helicopter will always land there
01391      * and get serviced at the same time - setting */
01392     if (_settings_game.order.serviceathelipad) {
01393       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01394         /* an excerpt of ServiceAircraft, without the invisibility stuff */
01395         v->date_of_last_service = _date;
01396         v->breakdowns_since_last_service = 0;
01397         v->reliability = v->GetEngine()->reliability;
01398         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01399       }
01400     }
01401     return;
01402   }
01403 
01404   if (v->current_order.IsType(OT_NOTHING)) return;
01405 
01406   /* if the block of the next position is busy, stay put */
01407   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01408 
01409   /* airport-road is free. We either have to go to another airport, or to the hangar
01410    * ---> start moving */
01411 
01412   bool go_to_hangar = false;
01413   switch (v->current_order.GetType()) {
01414     case OT_GOTO_STATION: // ready to fly to another airport
01415       break;
01416     case OT_GOTO_DEPOT:   // visit hangar for servicing, sale, etc.
01417       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01418       break;
01419     case OT_CONDITIONAL:
01420       /* In case of a conditional order we just have to wait a tick
01421        * longer, so the conditional order can actually be processed;
01422        * we should not clear the order as that makes us go nowhere. */
01423       return;
01424     default:  // orders have been deleted (no orders), goto depot and don't bother us
01425       v->current_order.Free();
01426       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01427   }
01428 
01429   if (go_to_hangar) {
01430     v->state = HANGAR;
01431   } else {
01432     /* airplane goto state takeoff, helicopter to helitakeoff */
01433     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01434   }
01435   AirportMove(v, apc);
01436 }
01437 
01438 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01439 {
01440   error("OK, you shouldn't be here, check your Airport Scheme!");
01441 }
01442 
01443 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01444 {
01445   PlayAircraftSound(v); // play takeoffsound for airplanes
01446   v->state = STARTTAKEOFF;
01447 }
01448 
01449 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01450 {
01451   v->state = ENDTAKEOFF;
01452   v->UpdateDeltaXY(INVALID_DIR);
01453 }
01454 
01455 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01456 {
01457   v->state = FLYING;
01458   /* get the next position to go to, differs per airport */
01459   AircraftNextAirportPos_and_Order(v);
01460 }
01461 
01462 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01463 {
01464   v->state = FLYING;
01465   v->UpdateDeltaXY(INVALID_DIR);
01466 
01467   /* get the next position to go to, differs per airport */
01468   AircraftNextAirportPos_and_Order(v);
01469 
01470   /* Send the helicopter to a hangar if needed for replacement */
01471   if (v->NeedsAutomaticServicing()) {
01472     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01473     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01474     cur_company.Restore();
01475   }
01476 }
01477 
01478 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01479 {
01480   Station *st = Station::Get(v->targetairport);
01481 
01482   /* Runway busy, not allowed to use this airstation or closed, circle. */
01483   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
01484     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01485      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01486      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01487     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01488     const AirportFTA *current = apc->layout[v->pos].next;
01489     while (current != NULL) {
01490       if (current->heading == landingtype) {
01491         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01492          * we don't want that for plane in air
01493          * hack for speed thingie */
01494         uint16 tcur_speed = v->cur_speed;
01495         uint16 tsubspeed = v->subspeed;
01496         if (!AirportHasBlock(v, current, apc)) {
01497           v->state = landingtype; // LANDING / HELILANDING
01498           /* it's a bit dirty, but I need to set position to next position, otherwise
01499            * if there are multiple runways, plane won't know which one it took (because
01500            * they all have heading LANDING). And also occupy that block! */
01501           v->pos = current->next_position;
01502           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01503           return;
01504         }
01505         v->cur_speed = tcur_speed;
01506         v->subspeed = tsubspeed;
01507       }
01508       current = current->next;
01509     }
01510   }
01511   v->state = FLYING;
01512   v->pos = apc->layout[v->pos].next_position;
01513 }
01514 
01515 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01516 {
01517   v->state = ENDLANDING;
01518   AircraftLandAirplane(v);  // maybe crash airplane
01519 
01520   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01521   if (v->NeedsAutomaticServicing()) {
01522     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01523     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01524     cur_company.Restore();
01525   }
01526 }
01527 
01528 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01529 {
01530   v->state = HELIENDLANDING;
01531   v->UpdateDeltaXY(INVALID_DIR);
01532 }
01533 
01534 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01535 {
01536   /* next block busy, don't do a thing, just wait */
01537   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01538 
01539   /* if going to terminal (OT_GOTO_STATION) choose one
01540    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01541    * 2. not going for terminal (but depot, no order),
01542    * --> get out of the way to the hangar. */
01543   if (v->current_order.IsType(OT_GOTO_STATION)) {
01544     if (AirportFindFreeTerminal(v, apc)) return;
01545   }
01546   v->state = HANGAR;
01547 
01548 }
01549 
01550 static void AircraftEventHandler_HeliEndLanding(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 helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01556    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01557    * 2. not going for terminal (but depot, no order),
01558    * --> get out of the way to the hangar IF there are terminals on the airport.
01559    * --> else TAKEOFF
01560    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01561    * must go to a hangar. */
01562   if (v->current_order.IsType(OT_GOTO_STATION)) {
01563     if (AirportFindFreeHelipad(v, apc)) return;
01564   }
01565   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01566 }
01567 
01573 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01575 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01576   AircraftEventHandler_General,        // TO_ALL         =  0
01577   AircraftEventHandler_InHangar,       // HANGAR         =  1
01578   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01579   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01580   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01581   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01582   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01583   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01584   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01585   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01586   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01587   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01588   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01589   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01590   AircraftEventHandler_Flying,         // FLYING         = 14
01591   AircraftEventHandler_Landing,        // LANDING        = 15
01592   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01593   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01594   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01595   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01596   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01597   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01598 };
01599 
01600 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01601 {
01602   /* we have left the previous block, and entered the new one. Free the previous block */
01603   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01604     Station *st = Station::Get(v->targetairport);
01605 
01606     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01607   }
01608 }
01609 
01610 static void AirportGoToNextPosition(Aircraft *v)
01611 {
01612   /* if aircraft is not in position, wait until it is */
01613   if (!AircraftController(v)) return;
01614 
01615   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01616 
01617   AirportClearBlock(v, apc);
01618   AirportMove(v, apc); // move aircraft to next position
01619 }
01620 
01621 /* gets pos from vehicle and next orders */
01622 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01623 {
01624   /* error handling */
01625   if (v->pos >= apc->nofelements) {
01626     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01627     assert(v->pos < apc->nofelements);
01628   }
01629 
01630   const AirportFTA *current = &apc->layout[v->pos];
01631   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01632   if (current->heading == v->state) {
01633     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01634     byte prev_state = v->state;
01635     _aircraft_state_handlers[v->state](v, apc);
01636     if (v->state != FLYING) v->previous_pos = prev_pos;
01637     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01638     return true;
01639   }
01640 
01641   v->previous_pos = v->pos; // save previous location
01642 
01643   /* there is only one choice to move to */
01644   if (current->next == NULL) {
01645     if (AirportSetBlocks(v, current, apc)) {
01646       v->pos = current->next_position;
01647       UpdateAircraftCache(v);
01648     } // move to next position
01649     return false;
01650   }
01651 
01652   /* there are more choices to choose from, choose the one that
01653    * matches our heading */
01654   do {
01655     if (v->state == current->heading || current->heading == TO_ALL) {
01656       if (AirportSetBlocks(v, current, apc)) {
01657         v->pos = current->next_position;
01658         UpdateAircraftCache(v);
01659       } // move to next position
01660       return false;
01661     }
01662     current = current->next;
01663   } while (current != NULL);
01664 
01665   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01666   NOT_REACHED();
01667 }
01668 
01670 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01671 {
01672   const AirportFTA *reference = &apc->layout[v->pos];
01673   const AirportFTA *next = &apc->layout[current_pos->next_position];
01674 
01675   /* same block, then of course we can move */
01676   if (apc->layout[current_pos->position].block != next->block) {
01677     const Station *st = Station::Get(v->targetairport);
01678     uint64 airport_flags = next->block;
01679 
01680     /* check additional possible extra blocks */
01681     if (current_pos != reference && current_pos->block != NOTHING_block) {
01682       airport_flags |= current_pos->block;
01683     }
01684 
01685     if (st->airport.flags & airport_flags) {
01686       v->cur_speed = 0;
01687       v->subspeed = 0;
01688       return true;
01689     }
01690   }
01691   return false;
01692 }
01693 
01701 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01702 {
01703   const AirportFTA *next = &apc->layout[current_pos->next_position];
01704   const AirportFTA *reference = &apc->layout[v->pos];
01705 
01706   /* if the next position is in another block, check it and wait until it is free */
01707   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01708     uint64 airport_flags = next->block;
01709     /* search for all all elements in the list with the same state, and blocks != N
01710      * this means more blocks should be checked/set */
01711     const AirportFTA *current = current_pos;
01712     if (current == reference) current = current->next;
01713     while (current != NULL) {
01714       if (current->heading == current_pos->heading && current->block != 0) {
01715         airport_flags |= current->block;
01716         break;
01717       }
01718       current = current->next;
01719     }
01720 
01721     /* if the block to be checked is in the next position, then exclude that from
01722      * checking, because it has been set by the airplane before */
01723     if (current_pos->block == next->block) airport_flags ^= next->block;
01724 
01725     Station *st = Station::Get(v->targetairport);
01726     if (st->airport.flags & airport_flags) {
01727       v->cur_speed = 0;
01728       v->subspeed = 0;
01729       return false;
01730     }
01731 
01732     if (next->block != NOTHING_block) {
01733       SETBITS(st->airport.flags, airport_flags); // occupy next block
01734     }
01735   }
01736   return true;
01737 }
01738 
01743 struct MovementTerminalMapping {
01744   AirportMovementStates state; 
01745   uint64 airport_flag;         
01746 };
01747 
01749 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01750   {TERM1, TERM1_block},
01751   {TERM2, TERM2_block},
01752   {TERM3, TERM3_block},
01753   {TERM4, TERM4_block},
01754   {TERM5, TERM5_block},
01755   {TERM6, TERM6_block},
01756   {TERM7, TERM7_block},
01757   {TERM8, TERM8_block},
01758   {HELIPAD1, HELIPAD1_block},
01759   {HELIPAD2, HELIPAD2_block},
01760   {HELIPAD3, HELIPAD3_block},
01761 };
01762 
01770 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01771 {
01772   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01773   Station *st = Station::Get(v->targetairport);
01774   for (; i < last_terminal; i++) {
01775     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01776       /* TERMINAL# HELIPAD# */
01777       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01778       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01779       return true;
01780     }
01781   }
01782   return false;
01783 }
01784 
01790 static uint GetNumTerminals(const AirportFTAClass *apc)
01791 {
01792   uint num = 0;
01793 
01794   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01795 
01796   return num;
01797 }
01798 
01805 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01806 {
01807   /* example of more terminalgroups
01808    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01809    * Heading 255 denotes a group. We see 2 groups here:
01810    * 1. group 0 -- TERM_GROUP1_block (check block)
01811    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01812    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01813    * looks at the corresponding terminals of that group. If no free ones are found, other
01814    * possible groups are checked (in this case group 1, since that is after group 0). If that
01815    * fails, then attempt fails and plane waits
01816    */
01817   if (apc->terminals[0] > 1) {
01818     const Station *st = Station::Get(v->targetairport);
01819     const AirportFTA *temp = apc->layout[v->pos].next;
01820 
01821     while (temp != NULL) {
01822       if (temp->heading == 255) {
01823         if (!(st->airport.flags & temp->block)) {
01824           /* read which group do we want to go to?
01825            * (the first free group) */
01826           uint target_group = temp->next_position + 1;
01827 
01828           /* at what terminal does the group start?
01829            * that means, sum up all terminals of
01830            * groups with lower number */
01831           uint group_start = 0;
01832           for (uint i = 1; i < target_group; i++) {
01833             group_start += apc->terminals[i];
01834           }
01835 
01836           uint group_end = group_start + apc->terminals[target_group];
01837           if (FreeTerminal(v, group_start, group_end)) return true;
01838         }
01839       } else {
01840         /* once the heading isn't 255, we've exhausted the possible blocks.
01841          * So we cannot move */
01842         return false;
01843       }
01844       temp = temp->next;
01845     }
01846   }
01847 
01848   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01849   return FreeTerminal(v, 0, GetNumTerminals(apc));
01850 }
01851 
01858 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01859 {
01860   /* if an airport doesn't have helipads, use terminals */
01861   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01862 
01863   /* only 1 helicoptergroup, check all helipads
01864    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01865   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01866 }
01867 
01873 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01874 {
01875   if (too_far) {
01876     if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01877       SetBit(v->flags, VAF_DEST_TOO_FAR);
01878       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01879       AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01880       if (v->owner == _local_company) {
01881         /* Post a news message. */
01882         SetDParam(0, v->index);
01883         AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
01884       }
01885     }
01886     return;
01887   }
01888 
01889   if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01890     /* Not too far anymore, clear flag and message. */
01891     ClrBit(v->flags, VAF_DEST_TOO_FAR);
01892     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01893     DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01894   }
01895 }
01896 
01897 static bool AircraftEventHandler(Aircraft *v, int loop)
01898 {
01899   v->tick_counter++;
01900 
01901   if (v->vehstatus & VS_CRASHED) {
01902     return HandleCrashedAircraft(v);
01903   }
01904 
01905   if (v->vehstatus & VS_STOPPED) return true;
01906 
01907   v->HandleBreakdown();
01908 
01909   HandleAircraftSmoke(v);
01910   ProcessOrders(v);
01911   v->HandleLoading(loop != 0);
01912 
01913   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01914 
01915   if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
01916     /* If we are flying, unconditionally clear the 'dest too far' state. */
01917     AircraftHandleDestTooFar(v, false);
01918   } else if (v->acache.cached_max_range_sqr != 0) {
01919     /* Check the distance to the next destination. This code works because the target
01920      * airport is only updated after take off and not on the ground. */
01921     Station *cur_st = Station::GetIfValid(v->targetairport);
01922     Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01923 
01924     if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01925       uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01926       AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01927     }
01928   }
01929 
01930   if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01931 
01932   return true;
01933 }
01934 
01935 bool Aircraft::Tick()
01936 {
01937   if (!this->IsNormalAircraft()) return true;
01938 
01939   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01940 
01941   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01942 
01943   this->current_order_time++;
01944 
01945   for (uint i = 0; i != 2; i++) {
01946     /* stop if the aircraft was deleted */
01947     if (!AircraftEventHandler(this, i)) return false;
01948   }
01949 
01950   return true;
01951 }
01952 
01953 
01960 Station *GetTargetAirportIfValid(const Aircraft *v)
01961 {
01962   assert(v->type == VEH_AIRCRAFT);
01963 
01964   Station *st = Station::GetIfValid(v->targetairport);
01965   if (st == NULL) return NULL;
01966 
01967   return st->airport.tile == INVALID_TILE ? NULL : st;
01968 }
01969 
01974 void UpdateAirplanesOnNewStation(const Station *st)
01975 {
01976   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01977   const AirportFTAClass *ap = st->airport.GetFTA();
01978   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01979 
01980   Aircraft *v;
01981   FOR_ALL_AIRCRAFT(v) {
01982     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01983     assert(v->state == FLYING);
01984     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01985     UpdateAircraftCache(v);
01986   }
01987 }