# aircraft_cmd.cpp

Go to the documentation of this file.

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