# aircraft_cmd.cpp

Go to the documentation of this file.

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