aircraft_cmd.cpp

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

Generated on Wed Dec 23 23:27:48 2009 for OpenTTD by  doxygen 1.5.6