aircraft_cmd.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 
01246 void AircraftLeaveHangar(Aircraft *v)
01247 {
01248   v->cur_speed = 0;
01249   v->subspeed = 0;
01250   v->progress = 0;
01251   v->direction = DIR_SE;
01252   v->vehstatus &= ~VS_HIDDEN;
01253   {
01254     Vehicle *u = v->Next();
01255     u->vehstatus &= ~VS_HIDDEN;
01256 
01257     /* Rotor blades */
01258     u = u->Next();
01259     if (u != NULL) {
01260       u->vehstatus &= ~VS_HIDDEN;
01261       u->cur_speed = 80;
01262     }
01263   }
01264 
01265   VehicleServiceInDepot(v);
01266   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01267   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01268   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01269 }
01270 
01274 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01275 {
01276   AircraftEntersTerminal(v);
01277   v->state = apc->layout[v->pos].heading;
01278 }
01279 
01285 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01286 {
01287   VehicleEnterDepot(v);
01288   v->state = apc->layout[v->pos].heading;
01289 }
01290 
01296 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01297 {
01298   /* if we just arrived, execute EnterHangar first */
01299   if (v->previous_pos != v->pos) {
01300     AircraftEventHandler_EnterHangar(v, apc);
01301     return;
01302   }
01303 
01304   /* if we were sent to the depot, stay there */
01305   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01306     v->current_order.Free();
01307     return;
01308   }
01309 
01310   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01311       !v->current_order.IsType(OT_GOTO_DEPOT))
01312     return;
01313 
01314   /* if the block of the next position is busy, stay put */
01315   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01316 
01317   /* We are already at the target airport, we need to find a terminal */
01318   if (v->current_order.GetDestination() == v->targetairport) {
01319     /* FindFreeTerminal:
01320      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01321     if (v->subtype == AIR_HELICOPTER) {
01322       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01323     } else {
01324       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01325     }
01326   } else { // Else prepare for launch.
01327     /* airplane goto state takeoff, helicopter to helitakeoff */
01328     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01329   }
01330   AircraftLeaveHangar(v);
01331   AirportMove(v, apc);
01332 }
01333 
01335 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01336 {
01337   /* if we just arrived, execute EnterTerminal first */
01338   if (v->previous_pos != v->pos) {
01339     AircraftEventHandler_EnterTerminal(v, apc);
01340     /* on an airport with helipads, a helicopter will always land there
01341      * and get serviced at the same time - setting */
01342     if (_settings_game.order.serviceathelipad) {
01343       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01344         /* an exerpt of ServiceAircraft, without the invisibility stuff */
01345         v->date_of_last_service = _date;
01346         v->breakdowns_since_last_service = 0;
01347         v->reliability = Engine::Get(v->engine_type)->reliability;
01348         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01349       }
01350     }
01351     return;
01352   }
01353 
01354   if (v->current_order.IsType(OT_NOTHING)) return;
01355 
01356   /* if the block of the next position is busy, stay put */
01357   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01358 
01359   /* airport-road is free. We either have to go to another airport, or to the hangar
01360    * ---> start moving */
01361 
01362   bool go_to_hangar = false;
01363   switch (v->current_order.GetType()) {
01364     case OT_GOTO_STATION: // ready to fly to another airport
01365       break;
01366     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
01367       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01368       break;
01369     case OT_CONDITIONAL:
01370       /* In case of a conditional order we just have to wait a tick
01371        * longer, so the conditional order can actually be processed;
01372        * we should not clear the order as that makes us go nowhere. */
01373       return;
01374     default:  // orders have been deleted (no orders), goto depot and don't bother us
01375       v->current_order.Free();
01376       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01377   }
01378 
01379   if (go_to_hangar) {
01380     v->state = HANGAR;
01381   } else {
01382     /* airplane goto state takeoff, helicopter to helitakeoff */
01383     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01384   }
01385   AirportMove(v, apc);
01386 }
01387 
01388 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01389 {
01390   error("OK, you shouldn't be here, check your Airport Scheme!");
01391 }
01392 
01393 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01394 {
01395   PlayAircraftSound(v); // play takeoffsound for airplanes
01396   v->state = STARTTAKEOFF;
01397 }
01398 
01399 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01400 {
01401   v->state = ENDTAKEOFF;
01402   v->UpdateDeltaXY(INVALID_DIR);
01403 }
01404 
01405 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01406 {
01407   v->state = FLYING;
01408   /* get the next position to go to, differs per airport */
01409   AircraftNextAirportPos_and_Order(v);
01410 }
01411 
01412 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01413 {
01414   v->state = FLYING;
01415   v->UpdateDeltaXY(INVALID_DIR);
01416 
01417   /* get the next position to go to, differs per airport */
01418   AircraftNextAirportPos_and_Order(v);
01419 
01420   /* Send the helicopter to a hangar if needed for replacement */
01421   if (v->NeedsAutomaticServicing()) {
01422     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01423     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01424     cur_company.Restore();
01425   }
01426 }
01427 
01428 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01429 {
01430   Station *st = Station::Get(v->targetairport);
01431 
01432   /* runway busy or not allowed to use this airstation, circle */
01433   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01434     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01435      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01436      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01437     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01438     const AirportFTA *current = apc->layout[v->pos].next;
01439     while (current != NULL) {
01440       if (current->heading == landingtype) {
01441         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01442          * we don't want that for plane in air
01443          * hack for speed thingie */
01444         uint16 tcur_speed = v->cur_speed;
01445         uint16 tsubspeed = v->subspeed;
01446         if (!AirportHasBlock(v, current, apc)) {
01447           v->state = landingtype; // LANDING / HELILANDING
01448           /* it's a bit dirty, but I need to set position to next position, otherwise
01449            * if there are multiple runways, plane won't know which one it took (because
01450            * they all have heading LANDING). And also occupy that block! */
01451           v->pos = current->next_position;
01452           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01453           return;
01454         }
01455         v->cur_speed = tcur_speed;
01456         v->subspeed = tsubspeed;
01457       }
01458       current = current->next;
01459     }
01460   }
01461   v->state = FLYING;
01462   v->pos = apc->layout[v->pos].next_position;
01463 }
01464 
01465 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01466 {
01467   v->state = ENDLANDING;
01468   AircraftLandAirplane(v);  // maybe crash airplane
01469 
01470   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01471   if (v->NeedsAutomaticServicing()) {
01472     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01473     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01474     cur_company.Restore();
01475   }
01476 }
01477 
01478 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01479 {
01480   v->state = HELIENDLANDING;
01481   v->UpdateDeltaXY(INVALID_DIR);
01482 }
01483 
01484 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01485 {
01486   /* next block busy, don't do a thing, just wait */
01487   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01488 
01489   /* if going to terminal (OT_GOTO_STATION) choose one
01490    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01491    * 2. not going for terminal (but depot, no order),
01492    * --> get out of the way to the hangar. */
01493   if (v->current_order.IsType(OT_GOTO_STATION)) {
01494     if (AirportFindFreeTerminal(v, apc)) return;
01495   }
01496   v->state = HANGAR;
01497 
01498 }
01499 
01500 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01501 {
01502   /*  next block busy, don't do a thing, just wait */
01503   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01504 
01505   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01506    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01507    * 2. not going for terminal (but depot, no order),
01508    * --> get out of the way to the hangar IF there are terminals on the airport.
01509    * --> else TAKEOFF
01510    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01511    * must go to a hangar. */
01512   if (v->current_order.IsType(OT_GOTO_STATION)) {
01513     if (AirportFindFreeHelipad(v, apc)) return;
01514   }
01515   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01516 }
01517 
01523 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01525 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01526   AircraftEventHandler_General,        // TO_ALL         =  0
01527   AircraftEventHandler_InHangar,       // HANGAR         =  1
01528   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01529   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01530   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01531   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01532   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01533   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01534   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01535   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01536   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01537   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01538   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01539   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01540   AircraftEventHandler_Flying,         // FLYING         = 14
01541   AircraftEventHandler_Landing,        // LANDING        = 15
01542   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01543   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01544   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01545   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01546   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01547   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01548 };
01549 
01550 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01551 {
01552   /* we have left the previous block, and entered the new one. Free the previous block */
01553   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01554     Station *st = Station::Get(v->targetairport);
01555 
01556     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01557   }
01558 }
01559 
01560 static void AirportGoToNextPosition(Aircraft *v)
01561 {
01562   /* if aircraft is not in position, wait until it is */
01563   if (!AircraftController(v)) return;
01564 
01565   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01566 
01567   AirportClearBlock(v, apc);
01568   AirportMove(v, apc); // move aircraft to next position
01569 }
01570 
01571 /* gets pos from vehicle and next orders */
01572 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01573 {
01574   /* error handling */
01575   if (v->pos >= apc->nofelements) {
01576     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01577     assert(v->pos < apc->nofelements);
01578   }
01579 
01580   const AirportFTA *current = &apc->layout[v->pos];
01581   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01582   if (current->heading == v->state) {
01583     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01584     byte prev_state = v->state;
01585     _aircraft_state_handlers[v->state](v, apc);
01586     if (v->state != FLYING) v->previous_pos = prev_pos;
01587     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01588     return true;
01589   }
01590 
01591   v->previous_pos = v->pos; // save previous location
01592 
01593   /* there is only one choice to move to */
01594   if (current->next == NULL) {
01595     if (AirportSetBlocks(v, current, apc)) {
01596       v->pos = current->next_position;
01597       UpdateAircraftCache(v);
01598     } // move to next position
01599     return false;
01600   }
01601 
01602   /* there are more choices to choose from, choose the one that
01603    * matches our heading */
01604   do {
01605     if (v->state == current->heading || current->heading == TO_ALL) {
01606       if (AirportSetBlocks(v, current, apc)) {
01607         v->pos = current->next_position;
01608         UpdateAircraftCache(v);
01609       } // move to next position
01610       return false;
01611     }
01612     current = current->next;
01613   } while (current != NULL);
01614 
01615   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01616   NOT_REACHED();
01617 }
01618 
01620 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01621 {
01622   const AirportFTA *reference = &apc->layout[v->pos];
01623   const AirportFTA *next = &apc->layout[current_pos->next_position];
01624 
01625   /* same block, then of course we can move */
01626   if (apc->layout[current_pos->position].block != next->block) {
01627     const Station *st = Station::Get(v->targetairport);
01628     uint64 airport_flags = next->block;
01629 
01630     /* check additional possible extra blocks */
01631     if (current_pos != reference && current_pos->block != NOTHING_block) {
01632       airport_flags |= current_pos->block;
01633     }
01634 
01635     if (st->airport.flags & airport_flags) {
01636       v->cur_speed = 0;
01637       v->subspeed = 0;
01638       return true;
01639     }
01640   }
01641   return false;
01642 }
01643 
01651 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01652 {
01653   const AirportFTA *next = &apc->layout[current_pos->next_position];
01654   const AirportFTA *reference = &apc->layout[v->pos];
01655 
01656   /* if the next position is in another block, check it and wait until it is free */
01657   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01658     uint64 airport_flags = next->block;
01659     /* search for all all elements in the list with the same state, and blocks != N
01660      * this means more blocks should be checked/set */
01661     const AirportFTA *current = current_pos;
01662     if (current == reference) current = current->next;
01663     while (current != NULL) {
01664       if (current->heading == current_pos->heading && current->block != 0) {
01665         airport_flags |= current->block;
01666         break;
01667       }
01668       current = current->next;
01669     }
01670 
01671     /* if the block to be checked is in the next position, then exclude that from
01672      * checking, because it has been set by the airplane before */
01673     if (current_pos->block == next->block) airport_flags ^= next->block;
01674 
01675     Station *st = Station::Get(v->targetairport);
01676     if (st->airport.flags & airport_flags) {
01677       v->cur_speed = 0;
01678       v->subspeed = 0;
01679       return false;
01680     }
01681 
01682     if (next->block != NOTHING_block) {
01683       SETBITS(st->airport.flags, airport_flags); // occupy next block
01684     }
01685   }
01686   return true;
01687 }
01688 
01693 struct MovementTerminalMapping {
01694   AirportMovementStates state; 
01695   uint64 airport_flag;         
01696 };
01697 
01699 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01700   {TERM1, TERM1_block},
01701   {TERM2, TERM2_block},
01702   {TERM3, TERM3_block},
01703   {TERM4, TERM4_block},
01704   {TERM5, TERM5_block},
01705   {TERM6, TERM6_block},
01706   {TERM7, TERM7_block},
01707   {TERM8, TERM8_block},
01708   {HELIPAD1, HELIPAD1_block},
01709   {HELIPAD2, HELIPAD2_block},
01710   {HELIPAD3, HELIPAD3_block},
01711 };
01712 
01720 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01721 {
01722   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01723   Station *st = Station::Get(v->targetairport);
01724   for (; i < last_terminal; i++) {
01725     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01726       /* TERMINAL# HELIPAD# */
01727       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01728       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01729       return true;
01730     }
01731   }
01732   return false;
01733 }
01734 
01740 static uint GetNumTerminals(const AirportFTAClass *apc)
01741 {
01742   uint num = 0;
01743 
01744   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01745 
01746   return num;
01747 }
01748 
01755 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01756 {
01757   /* example of more terminalgroups
01758    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01759    * Heading 255 denotes a group. We see 2 groups here:
01760    * 1. group 0 -- TERM_GROUP1_block (check block)
01761    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01762    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01763    * looks at the corresponding terminals of that group. If no free ones are found, other
01764    * possible groups are checked (in this case group 1, since that is after group 0). If that
01765    * fails, then attempt fails and plane waits
01766    */
01767   if (apc->terminals[0] > 1) {
01768     const Station *st = Station::Get(v->targetairport);
01769     const AirportFTA *temp = apc->layout[v->pos].next;
01770 
01771     while (temp != NULL) {
01772       if (temp->heading == 255) {
01773         if (!(st->airport.flags & temp->block)) {
01774           /* read which group do we want to go to?
01775            * (the first free group) */
01776           uint target_group = temp->next_position + 1;
01777 
01778           /* at what terminal does the group start?
01779            * that means, sum up all terminals of
01780            * groups with lower number */
01781           uint group_start = 0;
01782           for (uint i = 1; i < target_group; i++) {
01783             group_start += apc->terminals[i];
01784           }
01785 
01786           uint group_end = group_start + apc->terminals[target_group];
01787           if (FreeTerminal(v, group_start, group_end)) return true;
01788         }
01789       } else {
01790         /* once the heading isn't 255, we've exhausted the possible blocks.
01791          * So we cannot move */
01792         return false;
01793       }
01794       temp = temp->next;
01795     }
01796   }
01797 
01798   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01799   return FreeTerminal(v, 0, GetNumTerminals(apc));
01800 }
01801 
01808 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01809 {
01810   /* if an airport doesn't have helipads, use terminals */
01811   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01812 
01813   /* only 1 helicoptergroup, check all helipads
01814    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01815   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01816 }
01817 
01818 static bool AircraftEventHandler(Aircraft *v, int loop)
01819 {
01820   v->tick_counter++;
01821 
01822   if (v->vehstatus & VS_CRASHED) {
01823     return HandleCrashedAircraft(v);
01824   }
01825 
01826   if (v->vehstatus & VS_STOPPED) return true;
01827 
01828   v->HandleBreakdown();
01829 
01830   HandleAircraftSmoke(v);
01831   ProcessOrders(v);
01832   v->HandleLoading(loop != 0);
01833 
01834   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01835 
01836   AirportGoToNextPosition(v);
01837 
01838   return true;
01839 }
01840 
01841 bool Aircraft::Tick()
01842 {
01843   if (!this->IsNormalAircraft()) return true;
01844 
01845   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01846 
01847   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01848 
01849   this->current_order_time++;
01850 
01851   for (uint i = 0; i != 2; i++) {
01852     /* stop if the aircraft was deleted */
01853     if (!AircraftEventHandler(this, i)) return false;
01854   }
01855 
01856   return true;
01857 }
01858 
01859 
01866 Station *GetTargetAirportIfValid(const Aircraft *v)
01867 {
01868   assert(v->type == VEH_AIRCRAFT);
01869 
01870   Station *st = Station::GetIfValid(v->targetairport);
01871   if (st == NULL) return NULL;
01872 
01873   return st->airport.tile == INVALID_TILE ? NULL : st;
01874 }
01875 
01880 void UpdateAirplanesOnNewStation(const Station *st)
01881 {
01882   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01883   const AirportFTAClass *ap = st->airport.GetFTA();
01884   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01885 
01886   Aircraft *v;
01887   FOR_ALL_AIRCRAFT(v) {
01888     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01889     assert(v->state == FLYING);
01890     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01891     UpdateAircraftCache(v);
01892   }
01893 }

Generated on Thu Apr 14 00:48:11 2011 for OpenTTD by  doxygen 1.6.1