# aircraft_cmd.cpp

Go to the documentation of this file.

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