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