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