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 }

Generated on Mon Sep 22 20:34:14 2008 for openttd by  doxygen 1.5.6