# aircraft_cmd.cpp

Go to the documentation of this file.

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