# aircraft_cmd.cpp

Go to the documentation of this file.

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