# aircraft_cmd.cpp

Go to the documentation of this file.

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