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