aircraft_cmd.cpp

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

Generated on Sun May 15 19:20:06 2011 for OpenTTD by  doxygen 1.6.1