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