# aircraft_cmd.cpp

Go to the documentation of this file.

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