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