# 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->IsChainInDepot()) {
    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, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
    01168
    01169   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
    01170   SndPlayVehicleFx(SND_12_EXPLOSION, v);
    01171 }
    01172
    01177 static void MaybeCrashAirplane(Aircraft *v)
    01178 {
    01179   if (_settings_game.vehicle.plane_crashes == 0) return;
    01180
    01181   Station *st = Station::Get(v->targetairport);
    01182
    01183   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
    01184   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
    01185   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
    01186       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
    01187       !_cheats.no_jetcrash.value) {
    01188     prob /= 20;
    01189   } else {
    01190     prob /= 1500;
    01191   }
    01192
    01193   if (GB(Random(), 0, 22) > prob) return;
    01194
    01195   /* Crash the airplane. Remove all goods stored at the station. */
    01196   for (CargoID i = 0; i < NUM_CARGO; i++) {
    01197     st->goods[i].rating = 1;
    01198     st->goods[i].cargo.Truncate(0);
    01199   }
    01200
    01201   CrashAirplane(v);
    01202 }
    01203
    01209 static void AircraftEntersTerminal(Aircraft *v)
    01210 {
    01211   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
    01212
    01213   Station *st = Station::Get(v->targetairport);
    01214   v->last_station_visited = v->targetairport;
    01215
    01216   /* Check if station was ever visited before */
    01217   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
    01218     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
    01219     SetDParam(0, st->index);
    01220     /* show newsitem of celebrating citizens */
    01221     AddVehicleNewsItem(
    01222       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
    01223       (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
    01224       v->index,
    01225       st->index
    01226     );
    01227     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
    01228     Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
    01229   }
    01230
    01231   v->BeginLoading();
    01232 }
    01233
    01238 static void AircraftLandAirplane(Aircraft *v)
    01239 {
    01240   v->UpdateDeltaXY(INVALID_DIR);
    01241
    01242   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
    01243     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
    01244   }
    01245 }
    01246
    01247
    01249 void AircraftNextAirportPos_and_Order(Aircraft *v)
    01250 {
    01251   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
    01252     v->targetairport = v->current_order.GetDestination();
    01253   }
    01254
    01255   const Station *st = GetTargetAirportIfValid(v);
    01256   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
    01257   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
    01258   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
    01259 }
    01260
    01269 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
    01270 {
    01271   v->cur_speed = 0;
    01272   v->subspeed = 0;
    01273   v->progress = 0;
    01274   v->direction = exit_dir;
    01275   v->vehstatus &= ~VS_HIDDEN;
    01276   {
    01277     Vehicle *u = v->Next();
    01278     u->vehstatus &= ~VS_HIDDEN;
    01279
    01280     /* Rotor blades */
    01281     u = u->Next();
    01282     if (u != NULL) {
    01283       u->vehstatus &= ~VS_HIDDEN;
    01284       u->cur_speed = 80;
    01285     }
    01286   }
    01287
    01288   VehicleServiceInDepot(v);
    01289   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    01290   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01291   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01292 }
    01293
    01297 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
    01298 {
    01299   AircraftEntersTerminal(v);
    01300   v->state = apc->layout[v->pos].heading;
    01301 }
    01302
    01308 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
    01309 {
    01310   VehicleEnterDepot(v);
    01311   v->state = apc->layout[v->pos].heading;
    01312 }
    01313
    01319 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
    01320 {
    01321   /* if we just arrived, execute EnterHangar first */
    01322   if (v->previous_pos != v->pos) {
    01323     AircraftEventHandler_EnterHangar(v, apc);
    01324     return;
    01325   }
    01326
    01327   /* if we were sent to the depot, stay there */
    01328   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
    01329     v->current_order.Free();
    01330     return;
    01331   }
    01332
    01333   if (!v->current_order.IsType(OT_GOTO_STATION) &&
    01334       !v->current_order.IsType(OT_GOTO_DEPOT))
    01335     return;
    01336
    01337   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
    01338   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
    01339     VehicleEnterDepot(v);
    01340     return;
    01341   }
    01342
    01343   /* if the block of the next position is busy, stay put */
    01344   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01345
    01346   /* We are already at the target airport, we need to find a terminal */
    01347   if (v->current_order.GetDestination() == v->targetairport) {
    01348     /* FindFreeTerminal:
    01349      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
    01350     if (v->subtype == AIR_HELICOPTER) {
    01351       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
    01352     } else {
    01353       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
    01354     }
    01355   } else { // Else prepare for launch.
    01356     /* airplane goto state takeoff, helicopter to helitakeoff */
    01357     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01358   }
    01359   const Station *st = Station::GetByTile(v->tile);
    01360   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
    01361   AirportMove(v, apc);
    01362 }
    01363
    01365 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
    01366 {
    01367   /* if we just arrived, execute EnterTerminal first */
    01368   if (v->previous_pos != v->pos) {
    01369     AircraftEventHandler_EnterTerminal(v, apc);
    01370     /* on an airport with helipads, a helicopter will always land there
    01371      * and get serviced at the same time - setting */
    01372     if (_settings_game.order.serviceathelipad) {
    01373       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
    01374         /* an exerpt of ServiceAircraft, without the invisibility stuff */
    01375         v->date_of_last_service = _date;
    01376         v->breakdowns_since_last_service = 0;
    01377         v->reliability = v->GetEngine()->reliability;
    01378         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01379       }
    01380     }
    01381     return;
    01382   }
    01383
    01384   if (v->current_order.IsType(OT_NOTHING)) return;
    01385
    01386   /* if the block of the next position is busy, stay put */
    01387   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01388
    01389   /* airport-road is free. We either have to go to another airport, or to the hangar
    01390    * ---> start moving */
    01391
    01392   bool go_to_hangar = false;
    01393   switch (v->current_order.GetType()) {
    01394     case OT_GOTO_STATION: // ready to fly to another airport
    01395       break;
    01396     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
    01397       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
    01398       break;
    01399     case OT_CONDITIONAL:
    01400       /* In case of a conditional order we just have to wait a tick
    01401        * longer, so the conditional order can actually be processed;
    01402        * we should not clear the order as that makes us go nowhere. */
    01403       return;
    01404     default:  // orders have been deleted (no orders), goto depot and don't bother us
    01405       v->current_order.Free();
    01406       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
    01407   }
    01408
    01409   if (go_to_hangar) {
    01410     v->state = HANGAR;
    01411   } else {
    01412     /* airplane goto state takeoff, helicopter to helitakeoff */
    01413     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01414   }
    01415   AirportMove(v, apc);
    01416 }
    01417
    01418 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
    01419 {
    01420   error("OK, you shouldn't be here, check your Airport Scheme!");
    01421 }
    01422
    01423 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
    01424 {
    01425   PlayAircraftSound(v); // play takeoffsound for airplanes
    01426   v->state = STARTTAKEOFF;
    01427 }
    01428
    01429 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01430 {
    01431   v->state = ENDTAKEOFF;
    01432   v->UpdateDeltaXY(INVALID_DIR);
    01433 }
    01434
    01435 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01436 {
    01437   v->state = FLYING;
    01438   /* get the next position to go to, differs per airport */
    01439   AircraftNextAirportPos_and_Order(v);
    01440 }
    01441
    01442 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01443 {
    01444   v->state = FLYING;
    01445   v->UpdateDeltaXY(INVALID_DIR);
    01446
    01447   /* get the next position to go to, differs per airport */
    01448   AircraftNextAirportPos_and_Order(v);
    01449
    01450   /* Send the helicopter to a hangar if needed for replacement */
    01451   if (v->NeedsAutomaticServicing()) {
    01452     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01453     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
    01454     cur_company.Restore();
    01455   }
    01456 }
    01457
    01458 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
    01459 {
    01460   Station *st = Station::Get(v->targetairport);
    01461
    01462   /* Runway busy, not allowed to use this airstation or closed, circle. */
    01463   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
    01464     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
    01465      * if it is an airplane, look for LANDING, for helicopter HELILANDING
    01466      * it is possible to choose from multiple landing runways, so loop until a free one is found */
    01467     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
    01468     const AirportFTA *current = apc->layout[v->pos].next;
    01469     while (current != NULL) {
    01470       if (current->heading == landingtype) {
    01471         /* save speed before, since if AirportHasBlock is false, it resets them to 0
    01472          * we don't want that for plane in air
    01473          * hack for speed thingie */
    01474         uint16 tcur_speed = v->cur_speed;
    01475         uint16 tsubspeed = v->subspeed;
    01476         if (!AirportHasBlock(v, current, apc)) {
    01477           v->state = landingtype; // LANDING / HELILANDING
    01478           /* it's a bit dirty, but I need to set position to next position, otherwise
    01479            * if there are multiple runways, plane won't know which one it took (because
    01480            * they all have heading LANDING). And also occupy that block! */
    01481           v->pos = current->next_position;
    01482           SETBITS(st->airport.flags, apc->layout[v->pos].block);
    01483           return;
    01484         }
    01485         v->cur_speed = tcur_speed;
    01486         v->subspeed = tsubspeed;
    01487       }
    01488       current = current->next;
    01489     }
    01490   }
    01491   v->state = FLYING;
    01492   v->pos = apc->layout[v->pos].next_position;
    01493 }
    01494
    01495 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
    01496 {
    01497   v->state = ENDLANDING;
    01498   AircraftLandAirplane(v);  // maybe crash airplane
    01499
    01500   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
    01501   if (v->NeedsAutomaticServicing()) {
    01502     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01503     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
    01504     cur_company.Restore();
    01505   }
    01506 }
    01507
    01508 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
    01509 {
    01510   v->state = HELIENDLANDING;
    01511   v->UpdateDeltaXY(INVALID_DIR);
    01512 }
    01513
    01514 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
    01515 {
    01516   /* next block busy, don't do a thing, just wait */
    01517   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01518
    01519   /* if going to terminal (OT_GOTO_STATION) choose one
    01520    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
    01521    * 2. not going for terminal (but depot, no order),
    01522    * --> get out of the way to the hangar. */
    01523   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01524     if (AirportFindFreeTerminal(v, apc)) return;
    01525   }
    01526   v->state = HANGAR;
    01527
    01528 }
    01529
    01530 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
    01531 {
    01532   /*  next block busy, don't do a thing, just wait */
    01533   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01534
    01535   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
    01536    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
    01537    * 2. not going for terminal (but depot, no order),
    01538    * --> get out of the way to the hangar IF there are terminals on the airport.
    01539    * --> else TAKEOFF
    01540    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
    01541    * must go to a hangar. */
    01542   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01543     if (AirportFindFreeHelipad(v, apc)) return;
    01544   }
    01545   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
    01546 }
    01547
    01553 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
    01555 static AircraftStateHandler * const _aircraft_state_handlers[] = {
    01556   AircraftEventHandler_General,        // TO_ALL         =  0
    01557   AircraftEventHandler_InHangar,       // HANGAR         =  1
    01558   AircraftEventHandler_AtTerminal,     // TERM1          =  2
    01559   AircraftEventHandler_AtTerminal,     // TERM2          =  3
    01560   AircraftEventHandler_AtTerminal,     // TERM3          =  4
    01561   AircraftEventHandler_AtTerminal,     // TERM4          =  5
    01562   AircraftEventHandler_AtTerminal,     // TERM5          =  6
    01563   AircraftEventHandler_AtTerminal,     // TERM6          =  7
    01564   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
    01565   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
    01566   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
    01567   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
    01568   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
    01569   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
    01570   AircraftEventHandler_Flying,         // FLYING         = 14
    01571   AircraftEventHandler_Landing,        // LANDING        = 15
    01572   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
    01573   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
    01574   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
    01575   AircraftEventHandler_AtTerminal,     // TERM7          = 19
    01576   AircraftEventHandler_AtTerminal,     // TERM8          = 20
    01577   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
    01578 };
    01579
    01580 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
    01581 {
    01582   /* we have left the previous block, and entered the new one. Free the previous block */
    01583   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
    01584     Station *st = Station::Get(v->targetairport);
    01585
    01586     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
    01587   }
    01588 }
    01589
    01590 static void AirportGoToNextPosition(Aircraft *v)
    01591 {
    01592   /* if aircraft is not in position, wait until it is */
    01593   if (!AircraftController(v)) return;
    01594
    01595   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
    01596
    01597   AirportClearBlock(v, apc);
    01598   AirportMove(v, apc); // move aircraft to next position
    01599 }
    01600
    01601 /* gets pos from vehicle and next orders */
    01602 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
    01603 {
    01604   /* error handling */
    01605   if (v->pos >= apc->nofelements) {
    01606     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
    01607     assert(v->pos < apc->nofelements);
    01608   }
    01609
    01610   const AirportFTA *current = &apc->layout[v->pos];
    01611   /* we have arrived in an important state (eg terminal, hangar, etc.) */
    01612   if (current->heading == v->state) {
    01613     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
    01614     byte prev_state = v->state;
    01615     _aircraft_state_handlers[v->state](v, apc);
    01616     if (v->state != FLYING) v->previous_pos = prev_pos;
    01617     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
    01618     return true;
    01619   }
    01620
    01621   v->previous_pos = v->pos; // save previous location
    01622
    01623   /* there is only one choice to move to */
    01624   if (current->next == NULL) {
    01625     if (AirportSetBlocks(v, current, apc)) {
    01626       v->pos = current->next_position;
    01627       UpdateAircraftCache(v);
    01628     } // move to next position
    01629     return false;
    01630   }
    01631
    01632   /* there are more choices to choose from, choose the one that
    01633    * matches our heading */
    01634   do {
    01635     if (v->state == current->heading || current->heading == TO_ALL) {
    01636       if (AirportSetBlocks(v, current, apc)) {
    01637         v->pos = current->next_position;
    01638         UpdateAircraftCache(v);
    01639       } // move to next position
    01640       return false;
    01641     }
    01642     current = current->next;
    01643   } while (current != NULL);
    01644
    01645   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
    01646   NOT_REACHED();
    01647 }
    01648
    01650 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
    01651 {
    01652   const AirportFTA *reference = &apc->layout[v->pos];
    01653   const AirportFTA *next = &apc->layout[current_pos->next_position];
    01654
    01655   /* same block, then of course we can move */
    01656   if (apc->layout[current_pos->position].block != next->block) {
    01657     const Station *st = Station::Get(v->targetairport);
    01658     uint64 airport_flags = next->block;
    01659
    01660     /* check additional possible extra blocks */
    01661     if (current_pos != reference && current_pos->block != NOTHING_block) {
    01662       airport_flags |= current_pos->block;
    01663     }
    01664
    01665     if (st->airport.flags & airport_flags) {
    01666       v->cur_speed = 0;
    01667       v->subspeed = 0;
    01668       return true;
    01669     }
    01670   }
    01671   return false;
    01672 }
    01673
    01681 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
    01682 {
    01683   const AirportFTA *next = &apc->layout[current_pos->next_position];
    01684   const AirportFTA *reference = &apc->layout[v->pos];
    01685
    01686   /* if the next position is in another block, check it and wait until it is free */
    01687   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
    01688     uint64 airport_flags = next->block;
    01689     /* search for all all elements in the list with the same state, and blocks != N
    01690      * this means more blocks should be checked/set */
    01691     const AirportFTA *current = current_pos;
    01692     if (current == reference) current = current->next;
    01693     while (current != NULL) {
    01694       if (current->heading == current_pos->heading && current->block != 0) {
    01695         airport_flags |= current->block;
    01696         break;
    01697       }
    01698       current = current->next;
    01699     }
    01700
    01701     /* if the block to be checked is in the next position, then exclude that from
    01702      * checking, because it has been set by the airplane before */
    01703     if (current_pos->block == next->block) airport_flags ^= next->block;
    01704
    01705     Station *st = Station::Get(v->targetairport);
    01706     if (st->airport.flags & airport_flags) {
    01707       v->cur_speed = 0;
    01708       v->subspeed = 0;
    01709       return false;
    01710     }
    01711
    01712     if (next->block != NOTHING_block) {
    01713       SETBITS(st->airport.flags, airport_flags); // occupy next block
    01714     }
    01715   }
    01716   return true;
    01717 }
    01718
    01723 struct MovementTerminalMapping {
    01724   AirportMovementStates state;
    01725   uint64 airport_flag;
    01726 };
    01727
    01729 static const MovementTerminalMapping _airport_terminal_mapping[] = {
    01730   {TERM1, TERM1_block},
    01731   {TERM2, TERM2_block},
    01732   {TERM3, TERM3_block},
    01733   {TERM4, TERM4_block},
    01734   {TERM5, TERM5_block},
    01735   {TERM6, TERM6_block},
    01736   {TERM7, TERM7_block},
    01737   {TERM8, TERM8_block},
    01738   {HELIPAD1, HELIPAD1_block},
    01739   {HELIPAD2, HELIPAD2_block},
    01740   {HELIPAD3, HELIPAD3_block},
    01741 };
    01742
    01750 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
    01751 {
    01752   assert(last_terminal <= lengthof(_airport_terminal_mapping));
    01753   Station *st = Station::Get(v->targetairport);
    01754   for (; i < last_terminal; i++) {
    01755     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
    01756       /* TERMINAL# HELIPAD# */
    01757       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
    01758       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
    01759       return true;
    01760     }
    01761   }
    01762   return false;
    01763 }
    01764
    01770 static uint GetNumTerminals(const AirportFTAClass *apc)
    01771 {
    01772   uint num = 0;
    01773
    01774   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
    01775
    01776   return num;
    01777 }
    01778
    01785 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
    01786 {
    01787   /* example of more terminalgroups
    01788    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
    01789    * Heading 255 denotes a group. We see 2 groups here:
    01790    * 1. group 0 -- TERM_GROUP1_block (check block)
    01791    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
    01792    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
    01793    * looks at the corresponding terminals of that group. If no free ones are found, other
    01794    * possible groups are checked (in this case group 1, since that is after group 0). If that
    01795    * fails, then attempt fails and plane waits
    01796    */
    01797   if (apc->terminals[0] > 1) {
    01798     const Station *st = Station::Get(v->targetairport);
    01799     const AirportFTA *temp = apc->layout[v->pos].next;
    01800
    01801     while (temp != NULL) {
    01802       if (temp->heading == 255) {
    01803         if (!(st->airport.flags & temp->block)) {
    01804           /* read which group do we want to go to?
    01805            * (the first free group) */
    01806           uint target_group = temp->next_position + 1;
    01807
    01808           /* at what terminal does the group start?
    01809            * that means, sum up all terminals of
    01810            * groups with lower number */
    01811           uint group_start = 0;
    01812           for (uint i = 1; i < target_group; i++) {
    01813             group_start += apc->terminals[i];
    01814           }
    01815
    01816           uint group_end = group_start + apc->terminals[target_group];
    01817           if (FreeTerminal(v, group_start, group_end)) return true;
    01818         }
    01819       } else {
    01820         /* once the heading isn't 255, we've exhausted the possible blocks.
    01821          * So we cannot move */
    01822         return false;
    01823       }
    01824       temp = temp->next;
    01825     }
    01826   }
    01827
    01828   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
    01829   return FreeTerminal(v, 0, GetNumTerminals(apc));
    01830 }
    01831
    01838 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
    01839 {
    01840   /* if an airport doesn't have helipads, use terminals */
    01841   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
    01842
    01843   /* only 1 helicoptergroup, check all helipads
    01844    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
    01845   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
    01846 }
    01847
    01853 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
    01854 {
    01855   if (too_far) {
    01856     if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
    01857       SetBit(v->flags, VAF_DEST_TOO_FAR);
    01858       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
    01859       AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
    01860       if (v->owner == _local_company) {
    01861         /* Post a news message. */
    01862         SetDParam(0, v->index);
    01863         AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
    01864       }
    01865     }
    01866     return;
    01867   }
    01868
    01869   if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
    01870     /* Not too far anymore, clear flag and message. */
    01871     ClrBit(v->flags, VAF_DEST_TOO_FAR);
    01872     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
    01873     DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
    01874   }
    01875 }
    01876
    01877 static bool AircraftEventHandler(Aircraft *v, int loop)
    01878 {
    01879   v->tick_counter++;
    01880
    01881   if (v->vehstatus & VS_CRASHED) {
    01882     return HandleCrashedAircraft(v);
    01883   }
    01884
    01885   if (v->vehstatus & VS_STOPPED) return true;
    01886
    01887   v->HandleBreakdown();
    01888
    01889   HandleAircraftSmoke(v);
    01890   ProcessOrders(v);
    01891   v->HandleLoading(loop != 0);
    01892
    01893   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
    01894
    01895   if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
    01896     /* If we are flying, unconditionally clear the 'dest too far' state. */
    01897     AircraftHandleDestTooFar(v, false);
    01898   } else if (v->acache.cached_max_range_sqr != 0) {
    01899     /* Check the distance to the next destination. This code works because the target
    01900      * airport is only updated after take off and not on the ground. */
    01901     Station *cur_st = Station::GetIfValid(v->targetairport);
    01902     Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
    01903
    01904     if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
    01905       uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
    01906       AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
    01907     }
    01908   }
    01909
    01910   if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
    01911
    01912   return true;
    01913 }
    01914
    01915 bool Aircraft::Tick()
    01916 {
    01917   if (!this->IsNormalAircraft()) return true;
    01918
    01919   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
    01920
    01921   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
    01922
    01923   this->current_order_time++;
    01924
    01925   for (uint i = 0; i != 2; i++) {
    01926     /* stop if the aircraft was deleted */
    01927     if (!AircraftEventHandler(this, i)) return false;
    01928   }
    01929
    01930   return true;
    01931 }
    01932
    01933
    01940 Station *GetTargetAirportIfValid(const Aircraft *v)
    01941 {
    01942   assert(v->type == VEH_AIRCRAFT);
    01943
    01944   Station *st = Station::GetIfValid(v->targetairport);
    01945   if (st == NULL) return NULL;
    01946
    01947   return st->airport.tile == INVALID_TILE ? NULL : st;
    01948 }
    01949
    01954 void UpdateAirplanesOnNewStation(const Station *st)
    01955 {
    01956   /* only 1 station is updated per function call, so it is enough to get entry_point once */
    01957   const AirportFTAClass *ap = st->airport.GetFTA();
    01958   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
    01959
    01960   Aircraft *v;
    01961   FOR_ALL_AIRCRAFT(v) {
    01962     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
    01963     assert(v->state == FLYING);
    01964     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
    01965     UpdateAircraftCache(v);
    01966   }
    01967 }