aircraft_cmd.cpp

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

Generated on Sun Sep 13 08:19:14 2009 for OpenTTD by  doxygen 1.5.6