# aircraft_cmd.cpp

Go to the documentation of this file.

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