aircraft_cmd.cpp

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