# aircraft_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: aircraft_cmd.cpp 18616 2009-12-23 18:45:05Z 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 "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
    00039 #include "table/strings.h"
    00040 #include "table/sprites.h"
    00041
    00042 void Aircraft::UpdateDeltaXY(Direction direction)
    00043 {
    00044   uint32 x;
    00045 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
    00046   switch (this->subtype) {
    00047     default: NOT_REACHED();
    00048     case AIR_AIRCRAFT:
    00049     case AIR_HELICOPTER:
    00050       switch (this->state) {
    00051         case ENDTAKEOFF:
    00052         case LANDING:
    00053         case HELILANDING:
    00054         case FLYING:     x = MKIT(24, 24, -1, -1); break;
    00055         default:         x = MKIT( 2,  2, -1, -1); break;
    00056       }
    00057       this->z_extent = 5;
    00058       break;
    00059     case AIR_SHADOW:     this->z_extent = 1; x = MKIT(2,  2,  0,  0); break;
    00060     case AIR_ROTOR:      this->z_extent = 1; x = MKIT(2,  2, -1, -1); break;
    00061   }
    00062 #undef MKIT
    00063
    00064   this->x_offs        = GB(x,  0, 8);
    00065   this->y_offs        = GB(x,  8, 8);
    00066   this->x_extent      = GB(x, 16, 8);
    00067   this->y_extent      = GB(x, 24, 8);
    00068 }
    00069
    00070
    00073 static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
    00074 static const byte _airport_terminal_flag[] =  {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
    00075
    00076 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
    00077 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
    00078 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
    00079 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
    00080 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
    00081 static void CrashAirplane(Aircraft *v);
    00082
    00083 static const SpriteID _aircraft_sprite[] = {
    00084   0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
    00085   0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
    00086   0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
    00087   0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
    00088   0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
    00089   0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
    00090   0x0EBD, 0x0EC5
    00091 };
    00092
    00094 enum HelicopterRotorStates {
    00095   HRS_ROTOR_STOPPED,
    00096   HRS_ROTOR_MOVING_1,
    00097   HRS_ROTOR_MOVING_2,
    00098   HRS_ROTOR_MOVING_3,
    00099 };
    00100
    00107 static StationID FindNearestHangar(const Aircraft *v)
    00108 {
    00109   const Station *st;
    00110   uint best = 0;
    00111   StationID index = INVALID_STATION;
    00112   TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
    00113   const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
    00114
    00115   FOR_ALL_STATIONS(st) {
    00116     if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
    00117
    00118     const AirportFTAClass *afc = st->Airport();
    00119     if (afc->nof_depots == 0 || (
    00120           /* don't crash the plane if we know it can't land at the airport */
    00121           (afc->flags & AirportFTAClass::SHORT_STRIP) &&
    00122           (avi->subtype & AIR_FAST) &&
    00123           !_cheats.no_jetcrash.value)) {
    00124       continue;
    00125     }
    00126
    00127     /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
    00128     uint distance = DistanceSquare(vtile, st->airport_tile);
    00129     if (distance < best || index == INVALID_STATION) {
    00130       best = distance;
    00131       index = st->index;
    00132     }
    00133   }
    00134   return index;
    00135 }
    00136
    00137 #if 0
    00138
    00141 static bool HaveHangarInOrderList(Aircraft *v)
    00142 {
    00143   const Order *order;
    00144
    00145   FOR_VEHICLE_ORDERS(v, order) {
    00146     const Station *st = Station::Get(order->station);
    00147     if (st->owner == v->owner && (st->facilities & FACIL_AIRPORT)) {
    00148       /* If an airport doesn't have a hangar, skip it */
    00149       if (st->Airport()->nof_depots != 0)
    00150         return true;
    00151     }
    00152   }
    00153
    00154   return false;
    00155 }
    00156 #endif
    00157
    00158 SpriteID Aircraft::GetImage(Direction direction) const
    00159 {
    00160   uint8 spritenum = this->spritenum;
    00161
    00162   if (is_custom_sprite(spritenum)) {
    00163     SpriteID sprite = GetCustomVehicleSprite(this, direction);
    00164     if (sprite != 0) return sprite;
    00165
    00166     spritenum = Engine::Get(this->engine_type)->original_image_index;
    00167   }
    00168
    00169   return direction + _aircraft_sprite[spritenum];
    00170 }
    00171
    00172 SpriteID GetRotorImage(const Aircraft *v)
    00173 {
    00174   assert(v->subtype == AIR_HELICOPTER);
    00175
    00176   const Aircraft *w = v->Next()->Next();
    00177   if (is_custom_sprite(v->spritenum)) {
    00178     SpriteID sprite = GetCustomRotorSprite(v, false);
    00179     if (sprite != 0) return sprite;
    00180   }
    00181
    00182   /* Return standard rotor sprites if there are no custom sprites for this helicopter */
    00183   return SPR_ROTOR_STOPPED + w->state;
    00184 }
    00185
    00186 static SpriteID GetAircraftIcon(EngineID engine)
    00187 {
    00188   const Engine *e = Engine::Get(engine);
    00189   uint8 spritenum = e->u.air.image_index;
    00190
    00191   if (is_custom_sprite(spritenum)) {
    00192     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
    00193     if (sprite != 0) return sprite;
    00194
    00195     spritenum = e->original_image_index;
    00196   }
    00197
    00198   return DIR_W + _aircraft_sprite[spritenum];
    00199 }
    00200
    00201 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, SpriteID pal)
    00202 {
    00203   SpriteID sprite = GetAircraftIcon(engine);
    00204   const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
    00205   preferred_x = Clamp(preferred_x, left - real_sprite->x_offs, right - real_sprite->width - real_sprite->x_offs);
    00206   DrawSprite(sprite, pal, preferred_x, y);
    00207
    00208   if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
    00209     SpriteID rotor_sprite = GetCustomRotorIcon(engine);
    00210     if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
    00211     DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
    00212   }
    00213 }
    00214
    00220 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
    00221 {
    00222   const Sprite *spr = GetSprite(GetAircraftIcon(engine), ST_NORMAL);
    00223
    00224   width  = spr->width;
    00225   height = spr->height;
    00226 }
    00227
    00236 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00237 {
    00238   if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_ERROR_AIRCRAFT_NOT_AVAILABLE);
    00239
    00240   const Engine *e = Engine::Get(p1);
    00241   const AircraftVehicleInfo *avi = &e->u.air;
    00242   CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
    00243
    00244   /* Engines without valid cargo should not be available */
    00245   if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
    00246
    00247   /* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
    00248   if (flags & DC_QUERY_COST) return value;
    00249
    00250   if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
    00251
    00252   /* Prevent building aircraft types at places which can't handle them */
    00253   if (!CanVehicleUseStation(p1, Station::GetByTile(tile))) return CMD_ERROR;
    00254
    00255   /* We will need to allocate 2 or 3 vehicle structs, depending on type */
    00256   if (!Vehicle::CanAllocateItem(avi->subtype & AIR_CTOL ? 2 : 3)) {
    00257     return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
    00258   }
    00259
    00260   UnitID unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
    00261   if (unit_num > _settings_game.vehicle.max_aircraft)
    00262     return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
    00263
    00264   if (flags & DC_EXEC) {
    00265     Aircraft *v = new Aircraft(); // aircraft
    00266     Aircraft *u = new Aircraft(); // shadow
    00267
    00268     v->unitnumber = unit_num;
    00269     v->direction = DIR_SE;
    00270
    00271     v->owner = u->owner = _current_company;
    00272
    00273     v->tile = tile;
    00274 //    u->tile = 0;
    00275
    00276     uint x = TileX(tile) * TILE_SIZE + 5;
    00277     uint y = TileY(tile) * TILE_SIZE + 3;
    00278
    00279     v->x_pos = u->x_pos = x;
    00280     v->y_pos = u->y_pos = y;
    00281
    00282     u->z_pos = GetSlopeZ(x, y);
    00283     v->z_pos = u->z_pos + 1;
    00284
    00285     v->running_ticks = 0;
    00286
    00287 //    u->delta_x = u->delta_y = 0;
    00288
    00289     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
    00290     u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
    00291
    00292     v->spritenum = avi->image_index;
    00293 //    v->cargo_count = u->number_of_pieces = 0;
    00294
    00295     v->cargo_cap = avi->passenger_capacity;
    00296     u->cargo_cap = avi->mail_capacity;
    00297
    00298     v->cargo_type = e->GetDefaultCargoType();
    00299     u->cargo_type = CT_MAIL;
    00300
    00301     v->cargo_subtype = 0;
    00302
    00303     v->name = NULL;
    00304 //    v->next_order_param = v->next_order = 0;
    00305
    00306 //    v->time_counter = 0;
    00307 //    v->progress = 0;
    00308     v->last_station_visited = INVALID_STATION;
    00309 //    v->destination_coords = 0;
    00310
    00311     v->max_speed = avi->max_speed;
    00312     v->acceleration = avi->acceleration;
    00313     v->engine_type = p1;
    00314     u->engine_type = p1;
    00315
    00316     v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
    00317     v->UpdateDeltaXY(INVALID_DIR);
    00318     v->value = value.GetCost();
    00319
    00320     u->subtype = AIR_SHADOW;
    00321     u->UpdateDeltaXY(INVALID_DIR);
    00322
    00323     v->reliability = e->reliability;
    00324     v->reliability_spd_dec = e->reliability_spd_dec;
    00325     v->max_age = e->GetLifeLengthInDays();
    00326
    00327     _new_vehicle_id = v->index;
    00328
    00329     /* When we click on hangar we know the tile it is on. By that we know
    00330      * its position in the array of depots the airport has.....we can search
    00331      * layout for #th position of depot. Since layout must start with a listing
    00332      * of all depots, it is simple */
    00333     for (uint i = 0;; i++) {
    00334       const Station *st = Station::GetByTile(tile);
    00335       const AirportFTAClass *apc = st->Airport();
    00336
    00337       assert(i != apc->nof_depots);
    00338       if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
    00339         assert(apc->layout[i].heading == HANGAR);
    00340         v->pos = apc->layout[i].position;
    00341         break;
    00342       }
    00343     }
    00344
    00345     v->state = HANGAR;
    00346     v->previous_pos = v->pos;
    00347     v->targetairport = GetStationIndex(tile);
    00348     v->SetNext(u);
    00349
    00350     v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_aircraft;
    00351
    00352     v->date_of_last_service = _date;
    00353     v->build_year = u->build_year = _cur_year;
    00354
    00355     v->cur_image = u->cur_image = SPR_IMG_QUERY;
    00356
    00357     v->random_bits = VehicleRandomBits();
    00358     u->random_bits = VehicleRandomBits();
    00359
    00360     v->vehicle_flags = 0;
    00361     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
    00362
    00363     v->InvalidateNewGRFCacheOfChain();
    00364
    00365     v->cargo_cap = GetVehicleCapacity(v, &u->cargo_cap);
    00366
    00367     v->InvalidateNewGRFCacheOfChain();
    00368
    00369     UpdateAircraftCache(v);
    00370
    00371     VehicleMove(v, false);
    00372     VehicleMove(u, false);
    00373
    00374     /* Aircraft with 3 vehicles (chopper)? */
    00375     if (v->subtype == AIR_HELICOPTER) {
    00376       Aircraft *w = new Aircraft();
    00377       w->engine_type = p1;
    00378       w->direction = DIR_N;
    00379       w->owner = _current_company;
    00380       w->x_pos = v->x_pos;
    00381       w->y_pos = v->y_pos;
    00382       w->z_pos = v->z_pos + 5;
    00383       w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
    00384       w->spritenum = 0xFF;
    00385       w->subtype = AIR_ROTOR;
    00386       w->cur_image = SPR_ROTOR_STOPPED;
    00387       w->random_bits = VehicleRandomBits();
    00388       /* Use rotor's air.state to store the rotor animation frame */
    00389       w->state = HRS_ROTOR_STOPPED;
    00390       w->UpdateDeltaXY(INVALID_DIR);
    00391
    00392       u->SetNext(w);
    00393       VehicleMove(w, false);
    00394     }
    00395
    00396     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    00397     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
    00398     SetWindowDirty(WC_COMPANY, v->owner);
    00399     if (IsLocalCompany())
    00400       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Aircraft window
    00401
    00402     Company::Get(_current_company)->num_engines[p1]++;
    00403   }
    00404
    00405   return value;
    00406 }
    00407
    00408
    00417 CommandCost CmdSellAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00418 {
    00419   Aircraft *v = Aircraft::GetIfValid(p1);
    00420
    00421   if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
    00422   if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_AIRCRAFT_MUST_BE_STOPPED);
    00423
    00424   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_SELL_DESTROYED_VEHICLE);
    00425
    00426   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
    00427
    00428   if (flags & DC_EXEC) {
    00429     delete v;
    00430   }
    00431
    00432   return ret;
    00433 }
    00434
    00435 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
    00436 {
    00437   const Station *st = GetTargetAirportIfValid(this);
    00438   /* If the station is not a valid airport or if it has no hangars */
    00439   if (st == NULL || st->Airport()->nof_depots == 0) {
    00440     /* the aircraft has to search for a hangar on its own */
    00441     StationID station = FindNearestHangar(this);
    00442
    00443     if (station == INVALID_STATION) return false;
    00444
    00445     st = Station::Get(station);
    00446   }
    00447
    00448   if (location    != NULL) *location    = st->xy;
    00449   if (destination != NULL) *destination = st->index;
    00450
    00451   return true;
    00452 }
    00453
    00464 CommandCost CmdSendAircraftToHangar(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00465 {
    00466   if (p2 & DEPOT_MASS_SEND) {
    00467     /* Mass goto depot requested */
    00468     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
    00469     return SendAllVehiclesToDepot(VEH_AIRCRAFT, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
    00470   }
    00471
    00472   Aircraft *v = Aircraft::GetIfValid(p1);
    00473   if (v == NULL) return CMD_ERROR;
    00474
    00475   return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
    00476 }
    00477
    00478
    00490 CommandCost CmdRefitAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00491 {
    00492   byte new_subtype = GB(p2, 8, 8);
    00493
    00494   Aircraft *v = Aircraft::GetIfValid(p1);
    00495   if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
    00496   if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_AIRCRAFT_MUST_BE_STOPPED);
    00497   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_REFIT_DESTROYED_VEHICLE);
    00498
    00499   /* Check cargo */
    00500   CargoID new_cid = GB(p2, 0, 8);
    00501   if (new_cid >= NUM_CARGO) return CMD_ERROR;
    00502
    00503   CommandCost cost = RefitVehicle(v, true, new_cid, new_subtype, flags);
    00504
    00505   if (flags & DC_EXEC) {
    00506     v->colourmap = PAL_NONE; // invalidate vehicle colour map
    00507     SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00508     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    00509     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
    00510   }
    00511   v->InvalidateNewGRFCacheOfChain(); // always invalidate; querycost might have filled it
    00512
    00513   return cost;
    00514 }
    00515
    00516
    00517 static void CheckIfAircraftNeedsService(Aircraft *v)
    00518 {
    00519   if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
    00520   if (v->IsInDepot()) {
    00521     VehicleServiceInDepot(v);
    00522     return;
    00523   }
    00524
    00525   /* When we're parsing conditional orders and the like
    00526    * we don't want to consider going to a depot too. */
    00527   if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
    00528
    00529   const Station *st = Station::Get(v->current_order.GetDestination());
    00530
    00531   assert(st != NULL);
    00532
    00533   /* only goto depot if the target airport has terminals (eg. it is airport) */
    00534   if (st->airport_tile != INVALID_TILE && st->Airport()->terminals != NULL) {
    00535 //    printf("targetairport = %d, st->index = %d\n", v->targetairport, st->index);
    00536 //    v->targetairport = st->index;
    00537     v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
    00538     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00539   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    00540     v->current_order.MakeDummy();
    00541     SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00542   }
    00543 }
    00544
    00545 Money Aircraft::GetRunningCost() const
    00546 {
    00547   const Engine *e = Engine::Get(this->engine_type);
    00548   uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
    00549   return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->grffile);
    00550 }
    00551
    00552 void Aircraft::OnNewDay()
    00553 {
    00554   if (!this->IsNormalAircraft()) return;
    00555
    00556   if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
    00557
    00558   CheckOrders(this);
    00559
    00560   CheckVehicleBreakdown(this);
    00561   AgeVehicle(this);
    00562   CheckIfAircraftNeedsService(this);
    00563
    00564   if (this->running_ticks == 0) return;
    00565
    00566   CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
    00567
    00568   this->profit_this_year -= cost.GetCost();
    00569   this->running_ticks = 0;
    00570
    00571   SubtractMoneyFromCompanyFract(this->owner, cost);
    00572
    00573   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    00574   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    00575 }
    00576
    00577 static void HelicopterTickHandler(Aircraft *v)
    00578 {
    00579   Aircraft *u = v->Next()->Next();
    00580
    00581   if (u->vehstatus & VS_HIDDEN) return;
    00582
    00583   /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
    00584    * loading/unloading at a terminal or stopped */
    00585   if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
    00586     if (u->cur_speed != 0) {
    00587       u->cur_speed++;
    00588       if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
    00589         u->cur_speed = 0;
    00590       }
    00591     }
    00592   } else {
    00593     if (u->cur_speed == 0)
    00594       u->cur_speed = 0x70;
    00595
    00596     if (u->cur_speed >= 0x50)
    00597       u->cur_speed--;
    00598   }
    00599
    00600   int tick = ++u->tick_counter;
    00601   int spd = u->cur_speed >> 4;
    00602
    00603   SpriteID img;
    00604   if (spd == 0) {
    00605     u->state = HRS_ROTOR_STOPPED;
    00606     img = GetRotorImage(v);
    00607     if (u->cur_image == img) return;
    00608   } else if (tick >= spd) {
    00609     u->tick_counter = 0;
    00610     u->state++;
    00611     if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
    00612     img = GetRotorImage(v);
    00613   } else {
    00614     return;
    00615   }
    00616
    00617   u->cur_image = img;
    00618
    00619   VehicleMove(u, true);
    00620 }
    00621
    00622 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
    00623 {
    00624   v->x_pos = x;
    00625   v->y_pos = y;
    00626   v->z_pos = z;
    00627
    00628   v->UpdateViewport(true, false);
    00629   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
    00630
    00631   Aircraft *u = v->Next();
    00632
    00633   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
    00634   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
    00635   u->x_pos = x;
    00636   u->y_pos = y - ((v->z_pos - GetSlopeZ(safe_x, safe_y)) >> 3);;
    00637
    00638   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
    00639   u->z_pos = GetSlopeZ(safe_x, safe_y);
    00640   u->cur_image = v->cur_image;
    00641
    00642   VehicleMove(u, true);
    00643
    00644   u = u->Next();
    00645   if (u != NULL) {
    00646     u->x_pos = x;
    00647     u->y_pos = y;
    00648     u->z_pos = z + 5;
    00649
    00650     VehicleMove(u, true);
    00651   }
    00652 }
    00653
    00657 void HandleAircraftEnterHangar(Aircraft *v)
    00658 {
    00659   v->subspeed = 0;
    00660   v->progress = 0;
    00661
    00662   Aircraft *u = v->Next();
    00663   u->vehstatus |= VS_HIDDEN;
    00664   u = u->Next();
    00665   if (u != NULL) {
    00666     u->vehstatus |= VS_HIDDEN;
    00667     u->cur_speed = 0;
    00668   }
    00669
    00670   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    00671 }
    00672
    00673 static void PlayAircraftSound(const Vehicle *v)
    00674 {
    00675   if (!PlayVehicleSound(v, VSE_START)) {
    00676     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
    00677   }
    00678 }
    00679
    00680
    00681 void UpdateAircraftCache(Aircraft *v)
    00682 {
    00683   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
    00684   if (max_speed != 0) {
    00685     /* Convert from original units to (approx) km/h */
    00686     max_speed = (max_speed * 129) / 10;
    00687
    00688     v->acache.cached_max_speed = max_speed;
    00689   } else {
    00690     v->acache.cached_max_speed = 0xFFFF;
    00691   }
    00692 }
    00693
    00694
    00698 enum AircraftSpeedLimits {
    00699   SPEED_LIMIT_TAXI     =     50,
    00700   SPEED_LIMIT_APPROACH =    230,
    00701   SPEED_LIMIT_BROKEN   =    320,
    00702   SPEED_LIMIT_HOLD     =    425,
    00703   SPEED_LIMIT_NONE     = 0xFFFF
    00704 };
    00705
    00713 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
    00714 {
    00715   uint spd = v->acceleration * 16;
    00716   byte t;
    00717
    00718   /* Adjust speed limits by plane speed factor to prevent taxiing
    00719    * and take-off speeds being too low. */
    00720   speed_limit *= _settings_game.vehicle.plane_speed;
    00721
    00722   if (v->acache.cached_max_speed < speed_limit) {
    00723     if (v->cur_speed < speed_limit) hard_limit = false;
    00724     speed_limit = v->acache.cached_max_speed;
    00725   }
    00726
    00727   speed_limit = min(speed_limit, v->max_speed);
    00728
    00729   v->subspeed = (t = v->subspeed) + (byte)spd;
    00730
    00731   /* Aircraft's current speed is used twice so that very fast planes are
    00732    * forced to slow down rapidly in the short distance needed. The magic
    00733    * value 16384 was determined to give similar results to the old speed/48
    00734    * method at slower speeds. This also results in less reduction at slow
    00735    * speeds to that aircraft do not get to taxi speed straight after
    00736    * touchdown. */
    00737   if (!hard_limit && v->cur_speed > speed_limit) {
    00738     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
    00739   }
    00740
    00741   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
    00742
    00743   /* adjust speed for broken vehicles */
    00744   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
    00745
    00746   /* updates statusbar only if speed have changed to save CPU time */
    00747   if (spd != v->cur_speed) {
    00748     v->cur_speed = spd;
    00749     if (_settings_client.gui.vehicle_speed)
    00750       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00751   }
    00752
    00753   /* Adjust distance moved by plane speed setting */
    00754   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
    00755
    00756   if (!(v->direction & 1)) spd = spd * 3 / 4;
    00757
    00758   spd += v->progress;
    00759   v->progress = (byte)spd;
    00760   return spd >> 8;
    00761 }
    00762
    00770 byte GetAircraftFlyingAltitude(const Aircraft *v)
    00771 {
    00772   /* Make sure Aircraft fly no lower so that they don't conduct
    00773    * CFITs (controlled flight into terrain)
    00774    */
    00775   byte base_altitude = 150;
    00776
    00777   /* Make sure eastbound and westbound planes do not "crash" into each
    00778    * other by providing them with vertical seperation
    00779    */
    00780   switch (v->direction) {
    00781     case DIR_N:
    00782     case DIR_NE:
    00783     case DIR_E:
    00784     case DIR_SE:
    00785       base_altitude += 10;
    00786       break;
    00787
    00788     default: break;
    00789   }
    00790
    00791   /* Make faster planes fly higher so that they can overtake slower ones */
    00792   base_altitude += min(20 * (v->max_speed / 200), 90);
    00793
    00794   return base_altitude;
    00795 }
    00796
    00810 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc)
    00811 {
    00812   assert(v != NULL);
    00813   assert(apc != NULL);
    00814
    00815   /* In the case the station doesn't exit anymore, set target tile 0.
    00816    * It doesn't hurt much, aircraft will go to next order, nearest hangar
    00817    * or it will simply crash in next tick */
    00818   TileIndex tile = 0;
    00819
    00820   const Station *st = Station::GetIfValid(v->targetairport);
    00821   if (st != NULL) {
    00822     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
    00823     tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
    00824   }
    00825
    00826   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
    00827   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
    00828
    00829   DiagDirection dir;
    00830   if (abs(delta_y) < abs(delta_x)) {
    00831     /* We are northeast or southwest of the airport */
    00832     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
    00833   } else {
    00834     /* We are northwest or southeast of the airport */
    00835     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
    00836   }
    00837   return apc->entry_points[dir];
    00838 }
    00839
    00847 static bool AircraftController(Aircraft *v)
    00848 {
    00849   int count;
    00850
    00851   /* NULL if station is invalid */
    00852   const Station *st = Station::GetIfValid(v->targetairport);
    00853   /* INVALID_TILE if there is no station */
    00854   TileIndex tile = INVALID_TILE;
    00855   if (st != NULL) {
    00856     tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
    00857   }
    00858   /* DUMMY if there is no station or no airport */
    00859   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
    00860
    00861   /* prevent going to INVALID_TILE if airport is deleted. */
    00862   if (st == NULL || st->airport_tile == INVALID_TILE) {
    00863     /* Jump into our "holding pattern" state machine if possible */
    00864     if (v->pos >= afc->nofelements) {
    00865       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc);
    00866     } else if (v->targetairport != v->current_order.GetDestination()) {
    00867       /* If not possible, just get out of here fast */
    00868       v->state = FLYING;
    00869       UpdateAircraftCache(v);
    00870       AircraftNextAirportPos_and_Order(v);
    00871       /* get aircraft back on running altitude */
    00872       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
    00873       return false;
    00874     }
    00875   }
    00876
    00877   /*  get airport moving data */
    00878   const AirportMovingData *amd = afc->MovingData(v->pos);
    00879
    00880   int x = TileX(tile) * TILE_SIZE;
    00881   int y = TileY(tile) * TILE_SIZE;
    00882
    00883   /* Helicopter raise */
    00884   if (amd->flag & AMED_HELI_RAISE) {
    00885     Aircraft *u = v->Next()->Next();
    00886
    00887     /* Make sure the rotors don't rotate too fast */
    00888     if (u->cur_speed > 32) {
    00889       v->cur_speed = 0;
    00890       if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
    00891     } else {
    00892       u->cur_speed = 32;
    00893       count = UpdateAircraftSpeed(v);
    00894       if (count > 0) {
    00895         v->tile = 0;
    00896
    00897         /* Reached altitude? */
    00898         if (v->z_pos >= 184) {
    00899           v->cur_speed = 0;
    00900           return true;
    00901         }
    00902         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
    00903       }
    00904     }
    00905     return false;
    00906   }
    00907
    00908   /* Helicopter landing. */
    00909   if (amd->flag & AMED_HELI_LOWER) {
    00910     if (st == NULL) {
    00911       /* FIXME - AircraftController -> if station no longer exists, do not land
    00912        * helicopter will circle until sign disappears, then go to next order
    00913        * what to do when it is the only order left, right now it just stays in 1 place */
    00914       v->state = FLYING;
    00915       UpdateAircraftCache(v);
    00916       AircraftNextAirportPos_and_Order(v);
    00917       return false;
    00918     }
    00919
    00920     /* Vehicle is now at the airport. */
    00921     v->tile = tile;
    00922
    00923     /* Find altitude of landing position. */
    00924     int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
    00925
    00926     if (z == v->z_pos) {
    00927       Vehicle *u = v->Next()->Next();
    00928
    00929       /*  Increase speed of rotors. When speed is 80, we've landed. */
    00930       if (u->cur_speed >= 80) return true;
    00931       u->cur_speed += 4;
    00932     } else {
    00933       count = UpdateAircraftSpeed(v);
    00934       if (count > 0) {
    00935         if (v->z_pos > z) {
    00936           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
    00937         } else {
    00938           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
    00939         }
    00940       }
    00941     }
    00942     return false;
    00943   }
    00944
    00945   /* Get distance from destination pos to current pos. */
    00946   uint dist = abs(x + amd->x - v->x_pos) +  abs(y + amd->y - v->y_pos);
    00947
    00948   /* Need exact position? */
    00949   if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
    00950
    00951   /* At final pos? */
    00952   if (dist == 0) {
    00953     /* Change direction smoothly to final direction. */
    00954     DirDiff dirdiff = DirDifference(amd->direction, v->direction);
    00955     /* if distance is 0, and plane points in right direction, no point in calling
    00956      * UpdateAircraftSpeed(). So do it only afterwards */
    00957     if (dirdiff == DIRDIFF_SAME) {
    00958       v->cur_speed = 0;
    00959       return true;
    00960     }
    00961
    00962     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
    00963
    00964     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
    00965     v->cur_speed >>= 1;
    00966
    00967     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    00968     return false;
    00969   }
    00970
    00971   uint speed_limit = SPEED_LIMIT_TAXI;
    00972   bool hard_limit = true;
    00973
    00974   if (amd->flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
    00975   if (amd->flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
    00976   if (amd->flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
    00977   if (amd->flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
    00978
    00979   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
    00980   if (count == 0) return false;
    00981
    00982   if (v->time_counter != 0) v->time_counter--;
    00983
    00984   do {
    00985
    00986     GetNewVehiclePosResult gp;
    00987
    00988     if (dist < 4 || (amd->flag & AMED_LAND)) {
    00989       /* move vehicle one pixel towards target */
    00990       gp.x = (v->x_pos != (x + amd->x)) ?
    00991           v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
    00992           v->x_pos;
    00993       gp.y = (v->y_pos != (y + amd->y)) ?
    00994           v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
    00995           v->y_pos;
    00996
    00997       /* Oilrigs must keep v->tile as st->airport_tile, since the landing pad is in a non-airport tile */
    00998       gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
    00999
    01000     } else {
    01001
    01002       /* Turn. Do it slowly if in the air. */
    01003       Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
    01004       if (newdir != v->direction) {
    01005         if (amd->flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
    01006           if (v->time_counter == 0 || newdir == v->last_direction) {
    01007             if (newdir == v->last_direction) {
    01008               v->number_consecutive_turns = 0;
    01009             } else {
    01010               v->number_consecutive_turns++;
    01011             }
    01012             v->time_counter = 2 * _settings_game.vehicle.plane_speed;
    01013             v->last_direction = v->direction;
    01014             v->direction = newdir;
    01015           }
    01016
    01017           /* Move vehicle. */
    01018           gp = GetNewVehiclePos(v);
    01019         } else {
    01020           v->cur_speed >>= 1;
    01021           v->direction = newdir;
    01022
    01023           /* When leaving a terminal an aircraft often goes to a position
    01024            * directly in front of it. If it would move while turning it
    01025            * would need an two extra turns to end up at the correct position.
    01026            * To make it easier just disallow all moving while turning as
    01027            * long as an aircraft is on the ground. */
    01028           gp.x = v->x_pos;
    01029           gp.y = v->y_pos;
    01030           gp.new_tile = gp.old_tile = v->tile;
    01031         }
    01032       } else {
    01033         v->number_consecutive_turns = 0;
    01034         /* Move vehicle. */
    01035         gp = GetNewVehiclePos(v);
    01036       }
    01037     }
    01038
    01039     v->tile = gp.new_tile;
    01040     /* If vehicle is in the air, use tile coordinate 0. */
    01041     if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
    01042
    01043     /* Adjust Z for land or takeoff? */
    01044     uint z = v->z_pos;
    01045
    01046     if (amd->flag & AMED_TAKEOFF) {
    01047       z = min(z + 2, GetAircraftFlyingAltitude(v));
    01048     }
    01049
    01050     if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
    01051
    01052     if (amd->flag & AMED_LAND) {
    01053       if (st->airport_tile == INVALID_TILE) {
    01054         /* Airport has been removed, abort the landing procedure */
    01055         v->state = FLYING;
    01056         UpdateAircraftCache(v);
    01057         AircraftNextAirportPos_and_Order(v);
    01058         /* get aircraft back on running altitude */
    01059         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
    01060         continue;
    01061       }
    01062
    01063       uint curz = GetSlopeZ(x + amd->x, y + amd->y) + 1;
    01064
    01065       /* We're not flying below our destination, right? */
    01066       assert(curz <= z);
    01067       int t = max(1U, dist - 4);
    01068       int delta = z - curz;
    01069
    01070       /* Only start lowering when we're sufficiently close for a 1:1 glide */
    01071       if (delta >= t) {
    01072         z -= ((z - curz) + t - 1) / t;
    01073       }
    01074       if (z < curz) z = curz;
    01075     }
    01076
    01077     /* We've landed. Decrease speed when we're reaching end of runway. */
    01078     if (amd->flag & AMED_BRAKE) {
    01079       uint curz = GetSlopeZ(x, y) + 1;
    01080
    01081       if (z > curz) {
    01082         z--;
    01083       } else if (z < curz) {
    01084         z++;
    01085       }
    01086
    01087     }
    01088
    01089     SetAircraftPosition(v, gp.x, gp.y, z);
    01090   } while (--count != 0);
    01091   return false;
    01092 }
    01093
    01094
    01095 static bool HandleCrashedAircraft(Aircraft *v)
    01096 {
    01097   v->crashed_counter += 3;
    01098
    01099   Station *st = GetTargetAirportIfValid(v);
    01100
    01101   /* make aircraft crash down to the ground */
    01102   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
    01103     uint z = GetSlopeZ(v->x_pos, v->y_pos);
    01104     v->z_pos -= 1;
    01105     if (v->z_pos == z) {
    01106       v->crashed_counter = 500;
    01107       v->z_pos++;
    01108     }
    01109   }
    01110
    01111   if (v->crashed_counter < 650) {
    01112     uint32 r;
    01113     if (Chance16R(1, 32, r)) {
    01114       static const DirDiff delta[] = {
    01115         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
    01116       };
    01117
    01118       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
    01119       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    01120       r = Random();
    01121       CreateEffectVehicleRel(v,
    01122         GB(r, 0, 4) - 4,
    01123         GB(r, 4, 4) - 4,
    01124         GB(r, 8, 4),
    01125         EV_EXPLOSION_SMALL);
    01126     }
    01127   } else if (v->crashed_counter >= 10000) {
    01128     /*  remove rubble of crashed airplane */
    01129
    01130     /* clear runway-in on all airports, set by crashing plane
    01131      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
    01132      * but they all share the same number */
    01133     if (st != NULL) {
    01134       CLRBITS(st->airport_flags, RUNWAY_IN_block);
    01135       CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block); // commuter airport
    01136       CLRBITS(st->airport_flags, RUNWAY_IN2_block);    // intercontinental
    01137     }
    01138
    01139     delete v;
    01140
    01141     return false;
    01142   }
    01143
    01144   return true;
    01145 }
    01146
    01147 static void HandleBrokenAircraft(Aircraft *v)
    01148 {
    01149   if (v->breakdown_ctr != 1) {
    01150     v->breakdown_ctr = 1;
    01151     v->vehstatus |= VS_AIRCRAFT_BROKEN;
    01152
    01153     if (v->breakdowns_since_last_service != 255)
    01154       v->breakdowns_since_last_service++;
    01155     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    01156     SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01157   }
    01158 }
    01159
    01160
    01161 static void HandleAircraftSmoke(Aircraft *v)
    01162 {
    01163   static const struct {
    01164     int8 x;
    01165     int8 y;
    01166   } smoke_pos[] = {
    01167     {  5,  5 },
    01168     {  6,  0 },
    01169     {  5, -5 },
    01170     {  0, -6 },
    01171     { -5, -5 },
    01172     { -6,  0 },
    01173     { -5,  5 },
    01174     {  0,  6 }
    01175   };
    01176
    01177   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
    01178
    01179   if (v->cur_speed < 10) {
    01180     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
    01181     v->breakdown_ctr = 0;
    01182     return;
    01183   }
    01184
    01185   if ((v->tick_counter & 0x1F) == 0) {
    01186     CreateEffectVehicleRel(v,
    01187       smoke_pos[v->direction].x,
    01188       smoke_pos[v->direction].y,
    01189       2,
    01190       EV_SMOKE
    01191     );
    01192   }
    01193 }
    01194
    01195 void HandleMissingAircraftOrders(Aircraft *v)
    01196 {
    01197   /*
    01198    * We do not have an order. This can be divided into two cases:
    01199    * 1) we are heading to an invalid station. In this case we must
    01200    *    find another airport to go to. If there is nowhere to go,
    01201    *    we will destroy the aircraft as it otherwise will enter
    01202    *    the holding pattern for the first airport, which can cause
    01203    *    the plane to go into an undefined state when building an
    01204    *    airport with the same StationID.
    01205    * 2) we are (still) heading to a (still) valid airport, then we
    01206    *    can continue going there. This can happen when you are
    01207    *    changing the aircraft's orders while in-flight or in for
    01208    *    example a depot. However, when we have a current order to
    01209    *    go to a depot, we have to keep that order so the aircraft
    01210    *    actually stops.
    01211    */
    01212   const Station *st = GetTargetAirportIfValid(v);
    01213   if (st == NULL) {
    01214     CommandCost ret;
    01215     CompanyID old_company = _current_company;
    01216
    01217     _current_company = v->owner;
    01218     ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
    01219     _current_company = old_company;
    01220
    01221     if (CmdFailed(ret)) CrashAirplane(v);
    01222   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
    01223     v->current_order.Free();
    01224   }
    01225 }
    01226
    01227
    01228 TileIndex Aircraft::GetOrderStationLocation(StationID station)
    01229 {
    01230   /* Orders are changed in flight, ensure going to the right station. */
    01231   if (this->state == FLYING) {
    01232     AircraftNextAirportPos_and_Order(this);
    01233   }
    01234
    01235   /* Aircraft do not use dest-tile */
    01236   return 0;
    01237 }
    01238
    01239 void Aircraft::MarkDirty()
    01240 {
    01241   this->UpdateViewport(false, false);
    01242   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
    01243 }
    01244
    01245
    01246 uint Aircraft::Crash(bool flooded)
    01247 {
    01248   uint pass = Vehicle::Crash(flooded) + 2; // pilots
    01249   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
    01250
    01251   return pass;
    01252 }
    01253
    01254 static void CrashAirplane(Aircraft *v)
    01255 {
    01256   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
    01257
    01258   uint pass = v->Crash();
    01259   SetDParam(0, pass);
    01260
    01261   v->cargo.Truncate(0);
    01262   v->Next()->cargo.Truncate(0);
    01263   const Station *st = GetTargetAirportIfValid(v);
    01264   StringID newsitem;
    01265   AIEventVehicleCrashed::CrashReason crash_reason;
    01266   if (st == NULL) {
    01267     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
    01268     crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
    01269   } else {
    01270     SetDParam(1, st->index);
    01271     newsitem = STR_NEWS_AIRCRAFT_CRASH;
    01272     crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
    01273   }
    01274
    01275   AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
    01276
    01277   AddVehicleNewsItem(newsitem,
    01278     NS_ACCIDENT,
    01279     v->index,
    01280     st != NULL ? st->index : INVALID_STATION);
    01281
    01282   SndPlayVehicleFx(SND_12_EXPLOSION, v);
    01283 }
    01284
    01285 static void MaybeCrashAirplane(Aircraft *v)
    01286 {
    01287   Station *st = Station::Get(v->targetairport);
    01288
    01289   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
    01290   uint16 prob = 0x10000 / 1500;
    01291   if ((st->Airport()->flags & AirportFTAClass::SHORT_STRIP) &&
    01292       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
    01293       !_cheats.no_jetcrash.value) {
    01294     prob = 0x10000 / 20;
    01295   }
    01296
    01297   if (GB(Random(), 0, 16) > 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   MaybeCrashAirplane(v);
    01341 }
    01342
    01343
    01345 void AircraftNextAirportPos_and_Order(Aircraft *v)
    01346 {
    01347   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
    01348     v->targetairport = v->current_order.GetDestination();
    01349   }
    01350
    01351   const Station *st = GetTargetAirportIfValid(v);
    01352   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
    01353   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc);
    01354 }
    01355
    01356 void AircraftLeaveHangar(Aircraft *v)
    01357 {
    01358   v->cur_speed = 0;
    01359   v->subspeed = 0;
    01360   v->progress = 0;
    01361   v->direction = DIR_SE;
    01362   v->vehstatus &= ~VS_HIDDEN;
    01363   {
    01364     Vehicle *u = v->Next();
    01365     u->vehstatus &= ~VS_HIDDEN;
    01366
    01367     /* Rotor blades */
    01368     u = u->Next();
    01369     if (u != NULL) {
    01370       u->vehstatus &= ~VS_HIDDEN;
    01371       u->cur_speed = 80;
    01372     }
    01373   }
    01374
    01375   VehicleServiceInDepot(v);
    01376   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
    01377   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    01378   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
    01379 }
    01380
    01384 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
    01385 {
    01386   AircraftEntersTerminal(v);
    01387   v->state = apc->layout[v->pos].heading;
    01388 }
    01389
    01390 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
    01391 {
    01392   VehicleEnterDepot(v);
    01393   v->state = apc->layout[v->pos].heading;
    01394 }
    01395
    01397 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
    01398 {
    01399   /* if we just arrived, execute EnterHangar first */
    01400   if (v->previous_pos != v->pos) {
    01401     AircraftEventHandler_EnterHangar(v, apc);
    01402     return;
    01403   }
    01404
    01405   /* if we were sent to the depot, stay there */
    01406   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
    01407     v->current_order.Free();
    01408     return;
    01409   }
    01410
    01411   if (!v->current_order.IsType(OT_GOTO_STATION) &&
    01412       !v->current_order.IsType(OT_GOTO_DEPOT))
    01413     return;
    01414
    01415   /* if the block of the next position is busy, stay put */
    01416   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01417
    01418   /* We are already at the target airport, we need to find a terminal */
    01419   if (v->current_order.GetDestination() == v->targetairport) {
    01420     /* FindFreeTerminal:
    01421      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
    01422     if (v->subtype == AIR_HELICOPTER) {
    01423       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
    01424     } else {
    01425       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
    01426     }
    01427   } else { // Else prepare for launch.
    01428     /* airplane goto state takeoff, helicopter to helitakeoff */
    01429     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01430   }
    01431   AircraftLeaveHangar(v);
    01432   AirportMove(v, apc);
    01433 }
    01434
    01436 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
    01437 {
    01438   /* if we just arrived, execute EnterTerminal first */
    01439   if (v->previous_pos != v->pos) {
    01440     AircraftEventHandler_EnterTerminal(v, apc);
    01441     /* on an airport with helipads, a helicopter will always land there
    01442      * and get serviced at the same time - setting */
    01443     if (_settings_game.order.serviceathelipad) {
    01444       if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
    01445         /* an exerpt of ServiceAircraft, without the invisibility stuff */
    01446         v->date_of_last_service = _date;
    01447         v->breakdowns_since_last_service = 0;
    01448         v->reliability = Engine::Get(v->engine_type)->reliability;
    01449         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    01450       }
    01451     }
    01452     return;
    01453   }
    01454
    01455   if (v->current_order.IsType(OT_NOTHING)) return;
    01456
    01457   /* if the block of the next position is busy, stay put */
    01458   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01459
    01460   /* airport-road is free. We either have to go to another airport, or to the hangar
    01461    * ---> start moving */
    01462
    01463   bool go_to_hangar = false;
    01464   switch (v->current_order.GetType()) {
    01465     case OT_GOTO_STATION: // ready to fly to another airport
    01466       break;
    01467     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
    01468       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
    01469       break;
    01470     case OT_CONDITIONAL:
    01471       /* In case of a conditional order we just have to wait a tick
    01472        * longer, so the conditional order can actually be processed;
    01473        * we should not clear the order as that makes us go nowhere. */
    01474       return;
    01475     default:  // orders have been deleted (no orders), goto depot and don't bother us
    01476       v->current_order.Free();
    01477       go_to_hangar = Station::Get(v->targetairport)->Airport()->nof_depots != 0;
    01478   }
    01479
    01480   if (go_to_hangar) {
    01481     v->state = HANGAR;
    01482   } else {
    01483     /* airplane goto state takeoff, helicopter to helitakeoff */
    01484     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
    01485   }
    01486   AirportMove(v, apc);
    01487 }
    01488
    01489 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
    01490 {
    01491   error("OK, you shouldn't be here, check your Airport Scheme!");
    01492 }
    01493
    01494 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
    01495 {
    01496   PlayAircraftSound(v); // play takeoffsound for airplanes
    01497   v->state = STARTTAKEOFF;
    01498 }
    01499
    01500 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01501 {
    01502   v->state = ENDTAKEOFF;
    01503   v->UpdateDeltaXY(INVALID_DIR);
    01504 }
    01505
    01506 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01507 {
    01508   v->state = FLYING;
    01509   /* get the next position to go to, differs per airport */
    01510   AircraftNextAirportPos_and_Order(v);
    01511 }
    01512
    01513 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
    01514 {
    01515   v->state = FLYING;
    01516   v->UpdateDeltaXY(INVALID_DIR);
    01517
    01518   /* get the next position to go to, differs per airport */
    01519   AircraftNextAirportPos_and_Order(v);
    01520
    01521   /* Send the helicopter to a hangar if needed for replacement */
    01522   if (v->NeedsAutomaticServicing()) {
    01523     _current_company = v->owner;
    01524     DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
    01525     _current_company = OWNER_NONE;
    01526   }
    01527 }
    01528
    01529 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
    01530 {
    01531   Station *st = Station::Get(v->targetairport);
    01532
    01533   /* runway busy or not allowed to use this airstation, circle */
    01534   if ((apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES)) &&
    01535       st->airport_tile != INVALID_TILE &&
    01536       (st->owner == OWNER_NONE || st->owner == v->owner)) {
    01537     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
    01538      * if it is an airplane, look for LANDING, for helicopter HELILANDING
    01539      * it is possible to choose from multiple landing runways, so loop until a free one is found */
    01540     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
    01541     const AirportFTA *current = apc->layout[v->pos].next;
    01542     while (current != NULL) {
    01543       if (current->heading == landingtype) {
    01544         /* save speed before, since if AirportHasBlock is false, it resets them to 0
    01545          * we don't want that for plane in air
    01546          * hack for speed thingie */
    01547         uint16 tcur_speed = v->cur_speed;
    01548         uint16 tsubspeed = v->subspeed;
    01549         if (!AirportHasBlock(v, current, apc)) {
    01550           v->state = landingtype; // LANDING / HELILANDING
    01551           /* it's a bit dirty, but I need to set position to next position, otherwise
    01552            * if there are multiple runways, plane won't know which one it took (because
    01553            * they all have heading LANDING). And also occupy that block! */
    01554           v->pos = current->next_position;
    01555           SETBITS(st->airport_flags, apc->layout[v->pos].block);
    01556           return;
    01557         }
    01558         v->cur_speed = tcur_speed;
    01559         v->subspeed = tsubspeed;
    01560       }
    01561       current = current->next;
    01562     }
    01563   }
    01564   v->state = FLYING;
    01565   v->pos = apc->layout[v->pos].next_position;
    01566 }
    01567
    01568 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
    01569 {
    01570   v->state = ENDLANDING;
    01571   AircraftLandAirplane(v);  // maybe crash airplane
    01572
    01573   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
    01574   if (v->NeedsAutomaticServicing()) {
    01575     _current_company = v->owner;
    01576     DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
    01577     _current_company = OWNER_NONE;
    01578   }
    01579 }
    01580
    01581 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
    01582 {
    01583   v->state = HELIENDLANDING;
    01584   v->UpdateDeltaXY(INVALID_DIR);
    01585 }
    01586
    01587 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
    01588 {
    01589   /* next block busy, don't do a thing, just wait */
    01590   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01591
    01592   /* if going to terminal (OT_GOTO_STATION) choose one
    01593    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
    01594    * 2. not going for terminal (but depot, no order),
    01595    * --> get out of the way to the hangar. */
    01596   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01597     if (AirportFindFreeTerminal(v, apc)) return;
    01598   }
    01599   v->state = HANGAR;
    01600
    01601 }
    01602
    01603 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
    01604 {
    01605   /*  next block busy, don't do a thing, just wait */
    01606   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
    01607
    01608   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
    01609    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
    01610    * 2. not going for terminal (but depot, no order),
    01611    * --> get out of the way to the hangar IF there are terminals on the airport.
    01612    * --> else TAKEOFF
    01613    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
    01614    * must go to a hangar. */
    01615   if (v->current_order.IsType(OT_GOTO_STATION)) {
    01616     if (AirportFindFreeHelipad(v, apc)) return;
    01617   }
    01618   v->state = (apc->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 }