aircraft_cmd.cpp

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