# aircraft_cmd.cpp

Go to the documentation of this file.

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