aircraft_cmd.cpp

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

Generated on Sat Nov 20 20:59:01 2010 for OpenTTD by  doxygen 1.6.1