# aircraft_cmd.cpp

Go to the documentation of this file.

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