aircraft_cmd.cpp

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

Generated on Wed Mar 17 23:50:09 2010 for OpenTTD by  doxygen 1.6.1