aircraft_cmd.cpp

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