# aircraft_cmd.cpp

Go to the documentation of this file.

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