# aircraft_cmd.cpp

Go to the documentation of this file.

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