aircraft_cmd.cpp

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