# aircraft_cmd.cpp

Go to the documentation of this file.

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