# aircraft_cmd.cpp

Go to the documentation of this file.

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