aircraft_cmd.cpp

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