# 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
    01072 static void HandleAircraftSmoke(Aircraft *v)
    01073 {
    01074   static const struct {
    01075     int8 x;
    01076     int8 y;
    01077   } smoke_pos[] = {
    01078     {  5,  5 },
    01079     {  6,  0 },
    01080     {  5, -5 },
    01081     {  0, -6 },
    01082     { -5, -5 },
    01083     { -6,  0 },
    01084     { -5,  5 },
    01085     {  0,  6 }
    01086   };
    01087
    01088   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
    01089
    01090   if (v->cur_speed < 10) {
    01091     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
    01092     v->breakdown_ctr = 0;
    01093     return;
    01094   }
    01095
    01096   if ((v->tick_counter & 0x1F) == 0) {
    01097     CreateEffectVehicleRel(v,
    01098       smoke_pos[v->direction].x,
    01099       smoke_pos[v->direction].y,
    01100       2,
    01101       EV_BREAKDOWN_SMOKE_AIRCRAFT
    01102     );
    01103   }
    01104 }
    01105
    01106 void HandleMissingAircraftOrders(Aircraft *v)
    01107 {
    01108   /*
    01109    * We do not have an order. This can be divided into two cases:
    01110    * 1) we are heading to an invalid station. In this case we must
    01111    *    find another airport to go to. If there is nowhere to go,
    01112    *    we will destroy the aircraft as it otherwise will enter
    01113    *    the holding pattern for the first airport, which can cause
    01114    *    the plane to go into an undefined state when building an
    01115    *    airport with the same StationID.
    01116    * 2) we are (still) heading to a (still) valid airport, then we
    01117    *    can continue going there. This can happen when you are
    01118    *    changing the aircraft's orders while in-flight or in for
    01119    *    example a depot. However, when we have a current order to
    01120    *    go to a depot, we have to keep that order so the aircraft
    01121    *    actually stops.
    01122    */
    01123   const Station *st = GetTargetAirportIfValid(v);
    01124   if (st == NULL) {
    01125     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01126     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
    01127     cur_company.Restore();
    01128
    01129     if (ret.Failed()) CrashAirplane(v);
    01130   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
    01131     v->current_order.Free();
    01132   }
    01133 }
    01134
    01135
    01136 TileIndex Aircraft::GetOrderStationLocation(StationID station)
    01137 {
    01138   /* Orders are changed in flight, ensure going to the right station. */
    01139   if (this->state == FLYING) {
    01140     AircraftNextAirportPos_and_Order(this);
    01141   }
    01142
    01143   /* Aircraft do not use dest-tile */
    01144   return 0;
    01145 }
    01146
    01147 void Aircraft::MarkDirty()
    01148 {
    01149   this->UpdateViewport(false, false);
    01150   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
    01151 }
    01152
    01153
    01154 uint Aircraft::Crash(bool flooded)
    01155 {
    01156   uint pass = Vehicle::Crash(flooded) + 2; // pilots
    01157   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
    01158
    01159   return pass;
    01160 }
    01161
    01166 static void CrashAirplane(Aircraft *v)
    01167 {
    01168   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
    01169
    01170   uint pass = v->Crash();
    01171   SetDParam(0, pass);
    01172
    01173   v->cargo.Truncate();
    01174   v->Next()->cargo.Truncate();
    01175   const Station *st = GetTargetAirportIfValid(v);
    01176   StringID newsitem;
    01177   if (st == NULL) {
    01178     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
    01179   } else {
    01180     SetDParam(1, st->index);
    01181     newsitem = STR_NEWS_AIRCRAFT_CRASH;
    01182   }
    01183
    01184   AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
    01185   Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
    01186
    01187   AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
    01188
    01189   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
    01190   if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
    01191 }
    01192
    01197 static void MaybeCrashAirplane(Aircraft *v)
    01198 {
    01199   if (_settings_game.vehicle.plane_crashes == 0) return;
    01200
    01201   Station *st = Station::Get(v->targetairport);
    01202
    01203   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
    01204   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
    01205   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
    01206       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
    01207       !_cheats.no_jetcrash.value) {
    01208     prob /= 20;
    01209   } else {
    01210     prob /= 1500;
    01211   }
    01212
    01213   if (GB(Random(), 0, 22) > prob) return;
    01214
    01215   /* Crash the airplane. Remove all goods stored at the station. */
    01216   for (CargoID i = 0; i < NUM_CARGO; i++) {
    01217     st->goods[i].rating = 1;
    01218     st->goods[i].cargo.Truncate();
    01219   }
    01220
    01221   CrashAirplane(v);
    01222 }
    01223
    01229 static void AircraftEntersTerminal(Aircraft *v)
    01230 {
    01231   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
    01232
    01233   Station *st = Station::Get(v->targetairport);
    01234   v->last_station_visited = v->targetairport;
    01235
    01236   /* Check if station was ever visited before */
    01237   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
    01238     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
    01239     SetDParam(0, st->index);
    01240     /* show newsitem of celebrating citizens */
    01241     AddVehicleNewsItem(
    01242       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
    01243       (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
    01244       v->index,
    01245       st->index
    01246     );
    01247     AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
    01248     Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
    01249   }
    01250
    01251   v->BeginLoading();
    01252 }
    01253
    01258 static void AircraftLandAirplane(Aircraft *v)
    01259 {
    01260   v->UpdateDeltaXY(INVALID_DIR);
    01261
    01262   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
    01263     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
    01264   }
    01265 }
    01266
    01267
    01269 void AircraftNextAirportPos_and_Order(Aircraft *v)
    01270 {
    01271   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
    01272     v->targetairport = v->current_order.GetDestination();
    01273   }
    01274
    01275   const Station *st = GetTargetAirportIfValid(v);
    01276   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
    01277   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
    01278   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
    01279 }
    01280
    01289 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
    01290 {
    01291   v->cur_speed = 0;
    01292   v->subspeed = 0;
    01293   v->progress = 0;
    01294   v->direction = exit_dir;
    01295   v->vehstatus &= ~VS_HIDDEN;
    01296   {
    01297     Vehicle *u = v->Next();
    01298     u->vehstatus &= ~VS_HIDDEN;
    01299
    01300     /* Rotor blades */
    01301     u = u->Next();
    01302     if (u != NULL) {
    01303       u->vehstatus &= ~VS_HIDDEN;
    01304       u->cur_speed = 80;
    01305     }
    01306   }
    01307
    01308   VehicleServiceInDepot(v);
    01309   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    01310   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01311   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01312 }
    01313
    01317 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
    01318 {
    01319   AircraftEntersTerminal(v);
    01320   v->state = apc->layout[v->pos].heading;
    01321 }
    01322
    01328 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
    01329 {
    01330   VehicleEnterDepot(v);
    01331   v->state = apc->layout[v->pos].heading;
    01332 }
    01333
    01339 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
    01340 {
    01341   /* if we just arrived, execute EnterHangar first */
    01342   if (v->previous_pos != v->pos) {
    01343     AircraftEventHandler_EnterHangar(v, apc);
    01344     return;
    01345   }
    01346
    01347   /* if we were sent to the depot, stay there */
    01348   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
    01349     v->current_order.Free();
    01350     return;
    01351   }
    01352
    01353   if (!v->current_order.IsType(OT_GOTO_STATION) &&
    01354       !v->current_order.IsType(OT_GOTO_DEPOT))
    01355     return;
    01356
    01357   /* We are leaving a hangar, but have to go to the exact same one; re-enter */
    01358   if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
    01359     VehicleEnterDepot(v);
    01360     return;
    01361   }
    01362
    01363   /* if the block of the next position is busy, stay put */
    01364   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01365
    01366   /* We are already at the target airport, we need to find a terminal */
    01367   if (v->current_order.GetDestination() == v->targetairport) {
    01368     /* FindFreeTerminal:
    01369      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
    01370     if (v->subtype == AIR_HELICOPTER) {
    01371       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
    01372     } else {
    01373       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
    01374     }
    01375   } else { // Else prepare for launch.
    01376     /* airplane goto state takeoff, helicopter to helitakeoff */
    01377     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01378   }
    01379   const Station *st = Station::GetByTile(v->tile);
    01380   AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
    01381   AirportMove(v, apc);
    01382 }
    01383
    01385 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
    01386 {
    01387   /* if we just arrived, execute EnterTerminal first */
    01388   if (v->previous_pos != v->pos) {
    01389     AircraftEventHandler_EnterTerminal(v, apc);
    01390     /* on an airport with helipads, a helicopter will always land there
    01391      * and get serviced at the same time - setting */
    01392     if (_settings_game.order.serviceathelipad) {
    01393       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
    01394         /* an excerpt of ServiceAircraft, without the invisibility stuff */
    01395         v->date_of_last_service = _date;
    01396         v->breakdowns_since_last_service = 0;
    01397         v->reliability = v->GetEngine()->reliability;
    01398         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01399       }
    01400     }
    01401     return;
    01402   }
    01403
    01404   if (v->current_order.IsType(OT_NOTHING)) return;
    01405
    01406   /* if the block of the next position is busy, stay put */
    01407   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01408
    01409   /* airport-road is free. We either have to go to another airport, or to the hangar
    01410    * ---> start moving */
    01411
    01412   bool go_to_hangar = false;
    01413   switch (v->current_order.GetType()) {
    01414     case OT_GOTO_STATION: // ready to fly to another airport
    01415       break;
    01416     case OT_GOTO_DEPOT:   // visit hangar for servicing, sale, etc.
    01417       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
    01418       break;
    01419     case OT_CONDITIONAL:
    01420       /* In case of a conditional order we just have to wait a tick
    01421        * longer, so the conditional order can actually be processed;
    01422        * we should not clear the order as that makes us go nowhere. */
    01423       return;
    01424     default:  // orders have been deleted (no orders), goto depot and don't bother us
    01425       v->current_order.Free();
    01426       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
    01427   }
    01428
    01429   if (go_to_hangar) {
    01430     v->state = HANGAR;
    01431   } else {
    01432     /* airplane goto state takeoff, helicopter to helitakeoff */
    01433     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01434   }
    01435   AirportMove(v, apc);
    01436 }
    01437
    01438 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
    01439 {
    01440   error("OK, you shouldn't be here, check your Airport Scheme!");
    01441 }
    01442
    01443 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
    01444 {
    01445   PlayAircraftSound(v); // play takeoffsound for airplanes
    01446   v->state = STARTTAKEOFF;
    01447 }
    01448
    01449 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01450 {
    01451   v->state = ENDTAKEOFF;
    01452   v->UpdateDeltaXY(INVALID_DIR);
    01453 }
    01454
    01455 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01456 {
    01457   v->state = FLYING;
    01458   /* get the next position to go to, differs per airport */
    01459   AircraftNextAirportPos_and_Order(v);
    01460 }
    01461
    01462 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01463 {
    01464   v->state = FLYING;
    01465   v->UpdateDeltaXY(INVALID_DIR);
    01466
    01467   /* get the next position to go to, differs per airport */
    01468   AircraftNextAirportPos_and_Order(v);
    01469
    01470   /* Send the helicopter to a hangar if needed for replacement */
    01471   if (v->NeedsAutomaticServicing()) {
    01472     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01473     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
    01474     cur_company.Restore();
    01475   }
    01476 }
    01477
    01478 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
    01479 {
    01480   Station *st = Station::Get(v->targetairport);
    01481
    01482   /* Runway busy, not allowed to use this airstation or closed, circle. */
    01483   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
    01484     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
    01485      * if it is an airplane, look for LANDING, for helicopter HELILANDING
    01486      * it is possible to choose from multiple landing runways, so loop until a free one is found */
    01487     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
    01488     const AirportFTA *current = apc->layout[v->pos].next;
    01489     while (current != NULL) {
    01490       if (current->heading == landingtype) {
    01491         /* save speed before, since if AirportHasBlock is false, it resets them to 0
    01492          * we don't want that for plane in air
    01493          * hack for speed thingie */
    01494         uint16 tcur_speed = v->cur_speed;
    01495         uint16 tsubspeed = v->subspeed;
    01496         if (!AirportHasBlock(v, current, apc)) {
    01497           v->state = landingtype; // LANDING / HELILANDING
    01498           /* it's a bit dirty, but I need to set position to next position, otherwise
    01499            * if there are multiple runways, plane won't know which one it took (because
    01500            * they all have heading LANDING). And also occupy that block! */
    01501           v->pos = current->next_position;
    01502           SETBITS(st->airport.flags, apc->layout[v->pos].block);
    01503           return;
    01504         }
    01505         v->cur_speed = tcur_speed;
    01506         v->subspeed = tsubspeed;
    01507       }
    01508       current = current->next;
    01509     }
    01510   }
    01511   v->state = FLYING;
    01512   v->pos = apc->layout[v->pos].next_position;
    01513 }
    01514
    01515 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
    01516 {
    01517   v->state = ENDLANDING;
    01518   AircraftLandAirplane(v);  // maybe crash airplane
    01519
    01520   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
    01521   if (v->NeedsAutomaticServicing()) {
    01522     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
    01523     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
    01524     cur_company.Restore();
    01525   }
    01526 }
    01527
    01528 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
    01529 {
    01530   v->state = HELIENDLANDING;
    01531   v->UpdateDeltaXY(INVALID_DIR);
    01532 }
    01533
    01534 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
    01535 {
    01536   /* next block busy, don't do a thing, just wait */
    01537   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01538
    01539   /* if going to terminal (OT_GOTO_STATION) choose one
    01540    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
    01541    * 2. not going for terminal (but depot, no order),
    01542    * --> get out of the way to the hangar. */
    01543   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01544     if (AirportFindFreeTerminal(v, apc)) return;
    01545   }
    01546   v->state = HANGAR;
    01547
    01548 }
    01549
    01550 static void AircraftEventHandler_HeliEndLanding(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 helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
    01556    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
    01557    * 2. not going for terminal (but depot, no order),
    01558    * --> get out of the way to the hangar IF there are terminals on the airport.
    01559    * --> else TAKEOFF
    01560    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
    01561    * must go to a hangar. */
    01562   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01563     if (AirportFindFreeHelipad(v, apc)) return;
    01564   }
    01565   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
    01566 }
    01567
    01573 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
    01575 static AircraftStateHandler * const _aircraft_state_handlers[] = {
    01576   AircraftEventHandler_General,        // TO_ALL         =  0
    01577   AircraftEventHandler_InHangar,       // HANGAR         =  1
    01578   AircraftEventHandler_AtTerminal,     // TERM1          =  2
    01579   AircraftEventHandler_AtTerminal,     // TERM2          =  3
    01580   AircraftEventHandler_AtTerminal,     // TERM3          =  4
    01581   AircraftEventHandler_AtTerminal,     // TERM4          =  5
    01582   AircraftEventHandler_AtTerminal,     // TERM5          =  6
    01583   AircraftEventHandler_AtTerminal,     // TERM6          =  7
    01584   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
    01585   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
    01586   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
    01587   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
    01588   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
    01589   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
    01590   AircraftEventHandler_Flying,         // FLYING         = 14
    01591   AircraftEventHandler_Landing,        // LANDING        = 15
    01592   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
    01593   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
    01594   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
    01595   AircraftEventHandler_AtTerminal,     // TERM7          = 19
    01596   AircraftEventHandler_AtTerminal,     // TERM8          = 20
    01597   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
    01598 };
    01599
    01600 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
    01601 {
    01602   /* we have left the previous block, and entered the new one. Free the previous block */
    01603   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
    01604     Station *st = Station::Get(v->targetairport);
    01605
    01606     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
    01607   }
    01608 }
    01609
    01610 static void AirportGoToNextPosition(Aircraft *v)
    01611 {
    01612   /* if aircraft is not in position, wait until it is */
    01613   if (!AircraftController(v)) return;
    01614
    01615   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
    01616
    01617   AirportClearBlock(v, apc);
    01618   AirportMove(v, apc); // move aircraft to next position
    01619 }
    01620
    01621 /* gets pos from vehicle and next orders */
    01622 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
    01623 {
    01624   /* error handling */
    01625   if (v->pos >= apc->nofelements) {
    01626     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
    01627     assert(v->pos < apc->nofelements);
    01628   }
    01629
    01630   const AirportFTA *current = &apc->layout[v->pos];
    01631   /* we have arrived in an important state (eg terminal, hangar, etc.) */
    01632   if (current->heading == v->state) {
    01633     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
    01634     byte prev_state = v->state;
    01635     _aircraft_state_handlers[v->state](v, apc);
    01636     if (v->state != FLYING) v->previous_pos = prev_pos;
    01637     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
    01638     return true;
    01639   }
    01640
    01641   v->previous_pos = v->pos; // save previous location
    01642
    01643   /* there is only one choice to move to */
    01644   if (current->next == NULL) {
    01645     if (AirportSetBlocks(v, current, apc)) {
    01646       v->pos = current->next_position;
    01647       UpdateAircraftCache(v);
    01648     } // move to next position
    01649     return false;
    01650   }
    01651
    01652   /* there are more choices to choose from, choose the one that
    01653    * matches our heading */
    01654   do {
    01655     if (v->state == current->heading || current->heading == TO_ALL) {
    01656       if (AirportSetBlocks(v, current, apc)) {
    01657         v->pos = current->next_position;
    01658         UpdateAircraftCache(v);
    01659       } // move to next position
    01660       return false;
    01661     }
    01662     current = current->next;
    01663   } while (current != NULL);
    01664
    01665   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
    01666   NOT_REACHED();
    01667 }
    01668
    01670 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
    01671 {
    01672   const AirportFTA *reference = &apc->layout[v->pos];
    01673   const AirportFTA *next = &apc->layout[current_pos->next_position];
    01674
    01675   /* same block, then of course we can move */
    01676   if (apc->layout[current_pos->position].block != next->block) {
    01677     const Station *st = Station::Get(v->targetairport);
    01678     uint64 airport_flags = next->block;
    01679
    01680     /* check additional possible extra blocks */
    01681     if (current_pos != reference && current_pos->block != NOTHING_block) {
    01682       airport_flags |= current_pos->block;
    01683     }
    01684
    01685     if (st->airport.flags & airport_flags) {
    01686       v->cur_speed = 0;
    01687       v->subspeed = 0;
    01688       return true;
    01689     }
    01690   }
    01691   return false;
    01692 }
    01693
    01701 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
    01702 {
    01703   const AirportFTA *next = &apc->layout[current_pos->next_position];
    01704   const AirportFTA *reference = &apc->layout[v->pos];
    01705
    01706   /* if the next position is in another block, check it and wait until it is free */
    01707   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
    01708     uint64 airport_flags = next->block;
    01709     /* search for all all elements in the list with the same state, and blocks != N
    01710      * this means more blocks should be checked/set */
    01711     const AirportFTA *current = current_pos;
    01712     if (current == reference) current = current->next;
    01713     while (current != NULL) {
    01714       if (current->heading == current_pos->heading && current->block != 0) {
    01715         airport_flags |= current->block;
    01716         break;
    01717       }
    01718       current = current->next;
    01719     }
    01720
    01721     /* if the block to be checked is in the next position, then exclude that from
    01722      * checking, because it has been set by the airplane before */
    01723     if (current_pos->block == next->block) airport_flags ^= next->block;
    01724
    01725     Station *st = Station::Get(v->targetairport);
    01726     if (st->airport.flags & airport_flags) {
    01727       v->cur_speed = 0;
    01728       v->subspeed = 0;
    01729       return false;
    01730     }
    01731
    01732     if (next->block != NOTHING_block) {
    01733       SETBITS(st->airport.flags, airport_flags); // occupy next block
    01734     }
    01735   }
    01736   return true;
    01737 }
    01738
    01743 struct MovementTerminalMapping {
    01744   AirportMovementStates state;
    01745   uint64 airport_flag;
    01746 };
    01747
    01749 static const MovementTerminalMapping _airport_terminal_mapping[] = {
    01750   {TERM1, TERM1_block},
    01751   {TERM2, TERM2_block},
    01752   {TERM3, TERM3_block},
    01753   {TERM4, TERM4_block},
    01754   {TERM5, TERM5_block},
    01755   {TERM6, TERM6_block},
    01756   {TERM7, TERM7_block},
    01757   {TERM8, TERM8_block},
    01758   {HELIPAD1, HELIPAD1_block},
    01759   {HELIPAD2, HELIPAD2_block},
    01760   {HELIPAD3, HELIPAD3_block},
    01761 };
    01762
    01770 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
    01771 {
    01772   assert(last_terminal <= lengthof(_airport_terminal_mapping));
    01773   Station *st = Station::Get(v->targetairport);
    01774   for (; i < last_terminal; i++) {
    01775     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
    01776       /* TERMINAL# HELIPAD# */
    01777       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
    01778       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
    01779       return true;
    01780     }
    01781   }
    01782   return false;
    01783 }
    01784
    01790 static uint GetNumTerminals(const AirportFTAClass *apc)
    01791 {
    01792   uint num = 0;
    01793
    01794   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
    01795
    01796   return num;
    01797 }
    01798
    01805 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
    01806 {
    01807   /* example of more terminalgroups
    01808    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
    01809    * Heading 255 denotes a group. We see 2 groups here:
    01810    * 1. group 0 -- TERM_GROUP1_block (check block)
    01811    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
    01812    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
    01813    * looks at the corresponding terminals of that group. If no free ones are found, other
    01814    * possible groups are checked (in this case group 1, since that is after group 0). If that
    01815    * fails, then attempt fails and plane waits
    01816    */
    01817   if (apc->terminals[0] > 1) {
    01818     const Station *st = Station::Get(v->targetairport);
    01819     const AirportFTA *temp = apc->layout[v->pos].next;
    01820
    01821     while (temp != NULL) {
    01822       if (temp->heading == 255) {
    01823         if (!(st->airport.flags & temp->block)) {
    01824           /* read which group do we want to go to?
    01825            * (the first free group) */
    01826           uint target_group = temp->next_position + 1;
    01827
    01828           /* at what terminal does the group start?
    01829            * that means, sum up all terminals of
    01830            * groups with lower number */
    01831           uint group_start = 0;
    01832           for (uint i = 1; i < target_group; i++) {
    01833             group_start += apc->terminals[i];
    01834           }
    01835
    01836           uint group_end = group_start + apc->terminals[target_group];
    01837           if (FreeTerminal(v, group_start, group_end)) return true;
    01838         }
    01839       } else {
    01840         /* once the heading isn't 255, we've exhausted the possible blocks.
    01841          * So we cannot move */
    01842         return false;
    01843       }
    01844       temp = temp->next;
    01845     }
    01846   }
    01847
    01848   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
    01849   return FreeTerminal(v, 0, GetNumTerminals(apc));
    01850 }
    01851
    01858 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
    01859 {
    01860   /* if an airport doesn't have helipads, use terminals */
    01861   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
    01862
    01863   /* only 1 helicoptergroup, check all helipads
    01864    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
    01865   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
    01866 }
    01867
    01873 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
    01874 {
    01875   if (too_far) {
    01876     if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
    01877       SetBit(v->flags, VAF_DEST_TOO_FAR);
    01878       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
    01879       AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
    01880       if (v->owner == _local_company) {
    01881         /* Post a news message. */
    01882         SetDParam(0, v->index);
    01883         AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
    01884       }
    01885     }
    01886     return;
    01887   }
    01888
    01889   if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
    01890     /* Not too far anymore, clear flag and message. */
    01891     ClrBit(v->flags, VAF_DEST_TOO_FAR);
    01892     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
    01893     DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
    01894   }
    01895 }
    01896
    01897 static bool AircraftEventHandler(Aircraft *v, int loop)
    01898 {
    01899   v->tick_counter++;
    01900
    01901   if (v->vehstatus & VS_CRASHED) {
    01902     return HandleCrashedAircraft(v);
    01903   }
    01904
    01905   if (v->vehstatus & VS_STOPPED) return true;
    01906
    01907   v->HandleBreakdown();
    01908
    01909   HandleAircraftSmoke(v);
    01910   ProcessOrders(v);
    01911   v->HandleLoading(loop != 0);
    01912
    01913   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
    01914
    01915   if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
    01916     /* If we are flying, unconditionally clear the 'dest too far' state. */
    01917     AircraftHandleDestTooFar(v, false);
    01918   } else if (v->acache.cached_max_range_sqr != 0) {
    01919     /* Check the distance to the next destination. This code works because the target
    01920      * airport is only updated after take off and not on the ground. */
    01921     Station *cur_st = Station::GetIfValid(v->targetairport);
    01922     Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
    01923
    01924     if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
    01925       uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
    01926       AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
    01927     }
    01928   }
    01929
    01930   if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
    01931
    01932   return true;
    01933 }
    01934
    01935 bool Aircraft::Tick()
    01936 {
    01937   if (!this->IsNormalAircraft()) return true;
    01938
    01939   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
    01940
    01941   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
    01942
    01943   this->current_order_time++;
    01944
    01945   for (uint i = 0; i != 2; i++) {
    01946     /* stop if the aircraft was deleted */
    01947     if (!AircraftEventHandler(this, i)) return false;
    01948   }
    01949
    01950   return true;
    01951 }
    01952
    01953
    01960 Station *GetTargetAirportIfValid(const Aircraft *v)
    01961 {
    01962   assert(v->type == VEH_AIRCRAFT);
    01963
    01964   Station *st = Station::GetIfValid(v->targetairport);
    01965   if (st == NULL) return NULL;
    01966
    01967   return st->airport.tile == INVALID_TILE ? NULL : st;
    01968 }
    01969
    01974 void UpdateAirplanesOnNewStation(const Station *st)
    01975 {
    01976   /* only 1 station is updated per function call, so it is enough to get entry_point once */
    01977   const AirportFTAClass *ap = st->airport.GetFTA();
    01978   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
    01979
    01980   Aircraft *v;
    01981   FOR_ALL_AIRCRAFT(v) {
    01982     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
    01983     assert(v->state == FLYING);
    01984     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
    01985     UpdateAircraftCache(v);
    01986   }
    01987 }