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 }

Generated on Mon Mar 9 23:33:45 2009 for openttd by  doxygen 1.5.6