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

Generated on Fri Jun 3 05:18:48 2011 for OpenTTD by  doxygen 1.6.1