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