aircraft_cmd.cpp

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