# aircraft_cmd.cpp

Go to the documentation of this file.

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