aircraft_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: aircraft_cmd.cpp 21137 2010-11-10 22:56:05Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
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 "functions.h"
00030 #include "cheat_type.h"
00031 #include "company_base.h"
00032 #include "ai/ai.hpp"
00033 #include "company_func.h"
00034 #include "effectvehicle_func.h"
00035 #include "station_base.h"
00036 #include "engine_base.h"
00037 #include "core/random_func.hpp"
00038 #include "core/backup_type.hpp"
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 
00536 void UpdateAircraftCache(Aircraft *v)
00537 {
00538   uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00539   if (max_speed != 0) {
00540     /* Convert from original units to km-ish/h */
00541     max_speed = (max_speed * 128) / 10;
00542 
00543     v->vcache.cached_max_speed = max_speed;
00544   } else {
00545     /* Use the default max speed of the vehicle. */
00546     v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00547   }
00548 }
00549 
00550 
00554 enum AircraftSpeedLimits {
00555   SPEED_LIMIT_TAXI     =     50,  
00556   SPEED_LIMIT_APPROACH =    230,  
00557   SPEED_LIMIT_BROKEN   =    320,  
00558   SPEED_LIMIT_HOLD     =    425,  
00559   SPEED_LIMIT_NONE     = 0xFFFF   
00560 };
00561 
00569 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00570 {
00571   uint spd = v->acceleration * 16;
00572   byte t;
00573 
00574   /* Adjust speed limits by plane speed factor to prevent taxiing
00575    * and take-off speeds being too low. */
00576   speed_limit *= _settings_game.vehicle.plane_speed;
00577 
00578   if (v->vcache.cached_max_speed < speed_limit) {
00579     if (v->cur_speed < speed_limit) hard_limit = false;
00580     speed_limit = v->vcache.cached_max_speed;
00581   }
00582 
00583   v->subspeed = (t = v->subspeed) + (byte)spd;
00584 
00585   /* Aircraft's current speed is used twice so that very fast planes are
00586    * forced to slow down rapidly in the short distance needed. The magic
00587    * value 16384 was determined to give similar results to the old speed/48
00588    * method at slower speeds. This also results in less reduction at slow
00589    * speeds to that aircraft do not get to taxi speed straight after
00590    * touchdown. */
00591   if (!hard_limit && v->cur_speed > speed_limit) {
00592     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00593   }
00594 
00595   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00596 
00597   /* adjust speed for broken vehicles */
00598   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00599 
00600   /* updates statusbar only if speed have changed to save CPU time */
00601   if (spd != v->cur_speed) {
00602     v->cur_speed = spd;
00603     if (_settings_client.gui.vehicle_speed) {
00604       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00605     }
00606   }
00607 
00608   /* Adjust distance moved by plane speed setting */
00609   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00610 
00611   /* Convert direction-indepenent speed into direction-dependent speed. (old movement method) */
00612   spd = v->GetOldAdvanceSpeed(spd);
00613 
00614   spd += v->progress;
00615   v->progress = (byte)spd;
00616   return spd >> 8;
00617 }
00618 
00626 byte GetAircraftFlyingAltitude(const Aircraft *v)
00627 {
00628   if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00629 
00630   /* Make sure Aircraft fly no lower so that they don't conduct
00631    * CFITs (controlled flight into terrain)
00632    */
00633   byte base_altitude = PLANE_HOLDING_ALTITUDE;
00634 
00635   /* Make sure eastbound and westbound planes do not "crash" into each
00636    * other by providing them with vertical seperation
00637    */
00638   switch (v->direction) {
00639     case DIR_N:
00640     case DIR_NE:
00641     case DIR_E:
00642     case DIR_SE:
00643       base_altitude += 10;
00644       break;
00645 
00646     default: break;
00647   }
00648 
00649   /* Make faster planes fly higher so that they can overtake slower ones */
00650   base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00651 
00652   return base_altitude;
00653 }
00654 
00669 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00670 {
00671   assert(v != NULL);
00672   assert(apc != NULL);
00673 
00674   /* In the case the station doesn't exit anymore, set target tile 0.
00675    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00676    * or it will simply crash in next tick */
00677   TileIndex tile = 0;
00678 
00679   const Station *st = Station::GetIfValid(v->targetairport);
00680   if (st != NULL) {
00681     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00682     tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00683   }
00684 
00685   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00686   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00687 
00688   DiagDirection dir;
00689   if (abs(delta_y) < abs(delta_x)) {
00690     /* We are northeast or southwest of the airport */
00691     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00692   } else {
00693     /* We are northwest or southeast of the airport */
00694     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00695   }
00696   dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00697   return apc->entry_points[dir];
00698 }
00699 
00700 
00701 static void MaybeCrashAirplane(Aircraft *v);
00702 
00710 static bool AircraftController(Aircraft *v)
00711 {
00712   int count;
00713 
00714   /* NULL if station is invalid */
00715   const Station *st = Station::GetIfValid(v->targetairport);
00716   /* INVALID_TILE if there is no station */
00717   TileIndex tile = INVALID_TILE;
00718   Direction rotation = DIR_N;
00719   uint size_x = 1, size_y = 1;
00720   if (st != NULL) {
00721     if (st->airport.tile != INVALID_TILE) {
00722       tile = st->airport.tile;
00723       rotation = st->airport.rotation;
00724       size_x = st->airport.w;
00725       size_y = st->airport.h;
00726     } else {
00727       tile = st->xy;
00728     }
00729   }
00730   /* DUMMY if there is no station or no airport */
00731   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00732 
00733   /* prevent going to INVALID_TILE if airport is deleted. */
00734   if (st == NULL || st->airport.tile == INVALID_TILE) {
00735     /* Jump into our "holding pattern" state machine if possible */
00736     if (v->pos >= afc->nofelements) {
00737       v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00738     } else if (v->targetairport != v->current_order.GetDestination()) {
00739       /* If not possible, just get out of here fast */
00740       v->state = FLYING;
00741       UpdateAircraftCache(v);
00742       AircraftNextAirportPos_and_Order(v);
00743       /* get aircraft back on running altitude */
00744       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00745       return false;
00746     }
00747   }
00748 
00749   /*  get airport moving data */
00750   const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00751 
00752   int x = TileX(tile) * TILE_SIZE;
00753   int y = TileY(tile) * TILE_SIZE;
00754 
00755   /* Helicopter raise */
00756   if (amd.flag & AMED_HELI_RAISE) {
00757     Aircraft *u = v->Next()->Next();
00758 
00759     /* Make sure the rotors don't rotate too fast */
00760     if (u->cur_speed > 32) {
00761       v->cur_speed = 0;
00762       if (--u->cur_speed == 32) {
00763         if (!PlayVehicleSound(v, VSE_START)) {
00764           SndPlayVehicleFx(SND_18_HELICOPTER, v);
00765         }
00766       }
00767     } else {
00768       u->cur_speed = 32;
00769       count = UpdateAircraftSpeed(v);
00770       if (count > 0) {
00771         v->tile = 0;
00772         byte z_dest = GetAircraftFlyingAltitude(v);
00773 
00774         /* Reached altitude? */
00775         if (v->z_pos >= z_dest) {
00776           v->cur_speed = 0;
00777           return true;
00778         }
00779         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00780       }
00781     }
00782     return false;
00783   }
00784 
00785   /* Helicopter landing. */
00786   if (amd.flag & AMED_HELI_LOWER) {
00787     if (st == NULL) {
00788       /* FIXME - AircraftController -> if station no longer exists, do not land
00789        * helicopter will circle until sign disappears, then go to next order
00790        * what to do when it is the only order left, right now it just stays in 1 place */
00791       v->state = FLYING;
00792       UpdateAircraftCache(v);
00793       AircraftNextAirportPos_and_Order(v);
00794       return false;
00795     }
00796 
00797     /* Vehicle is now at the airport. */
00798     v->tile = tile;
00799 
00800     /* Find altitude of landing position. */
00801     int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00802 
00803     if (z == v->z_pos) {
00804       Vehicle *u = v->Next()->Next();
00805 
00806       /*  Increase speed of rotors. When speed is 80, we've landed. */
00807       if (u->cur_speed >= 80) return true;
00808       u->cur_speed += 4;
00809     } else {
00810       count = UpdateAircraftSpeed(v);
00811       if (count > 0) {
00812         if (v->z_pos > z) {
00813           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00814         } else {
00815           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00816         }
00817       }
00818     }
00819     return false;
00820   }
00821 
00822   /* Get distance from destination pos to current pos. */
00823   uint dist = abs(x + amd.x - v->x_pos) +  abs(y + amd.y - v->y_pos);
00824 
00825   /* Need exact position? */
00826   if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00827 
00828   /* At final pos? */
00829   if (dist == 0) {
00830     /* Change direction smoothly to final direction. */
00831     DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00832     /* if distance is 0, and plane points in right direction, no point in calling
00833      * UpdateAircraftSpeed(). So do it only afterwards */
00834     if (dirdiff == DIRDIFF_SAME) {
00835       v->cur_speed = 0;
00836       return true;
00837     }
00838 
00839     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00840 
00841     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00842     v->cur_speed >>= 1;
00843 
00844     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00845     return false;
00846   }
00847 
00848   if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00849     MaybeCrashAirplane(v);
00850     if ((v->vehstatus & VS_CRASHED) != 0) return false;
00851   }
00852 
00853   uint speed_limit = SPEED_LIMIT_TAXI;
00854   bool hard_limit = true;
00855 
00856   if (amd.flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
00857   if (amd.flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
00858   if (amd.flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00859   if (amd.flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
00860 
00861   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00862   if (count == 0) return false;
00863 
00864   if (v->turn_counter != 0) v->turn_counter--;
00865 
00866   do {
00867 
00868     GetNewVehiclePosResult gp;
00869 
00870     if (dist < 4 || (amd.flag & AMED_LAND)) {
00871       /* move vehicle one pixel towards target */
00872       gp.x = (v->x_pos != (x + amd.x)) ?
00873           v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00874           v->x_pos;
00875       gp.y = (v->y_pos != (y + amd.y)) ?
00876           v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00877           v->y_pos;
00878 
00879       /* Oilrigs must keep v->tile as st->airport.tile, since the landing pad is in a non-airport tile */
00880       gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00881 
00882     } else {
00883 
00884       /* Turn. Do it slowly if in the air. */
00885       Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00886       if (newdir != v->direction) {
00887         if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00888           if (v->turn_counter == 0 || newdir == v->last_direction) {
00889             if (newdir == v->last_direction) {
00890               v->number_consecutive_turns = 0;
00891             } else {
00892               v->number_consecutive_turns++;
00893             }
00894             v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00895             v->last_direction = v->direction;
00896             v->direction = newdir;
00897           }
00898 
00899           /* Move vehicle. */
00900           gp = GetNewVehiclePos(v);
00901         } else {
00902           v->cur_speed >>= 1;
00903           v->direction = newdir;
00904 
00905           /* When leaving a terminal an aircraft often goes to a position
00906            * directly in front of it. If it would move while turning it
00907            * would need an two extra turns to end up at the correct position.
00908            * To make it easier just disallow all moving while turning as
00909            * long as an aircraft is on the ground. */
00910           gp.x = v->x_pos;
00911           gp.y = v->y_pos;
00912           gp.new_tile = gp.old_tile = v->tile;
00913         }
00914       } else {
00915         v->number_consecutive_turns = 0;
00916         /* Move vehicle. */
00917         gp = GetNewVehiclePos(v);
00918       }
00919     }
00920 
00921     v->tile = gp.new_tile;
00922     /* If vehicle is in the air, use tile coordinate 0. */
00923     if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00924 
00925     /* Adjust Z for land or takeoff? */
00926     uint z = v->z_pos;
00927 
00928     if (amd.flag & AMED_TAKEOFF) {
00929       z = min(z + 2, GetAircraftFlyingAltitude(v));
00930     }
00931 
00932     /* Let the plane drop from normal flight altitude to holding pattern altitude */
00933     if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00934 
00935     if (amd.flag & AMED_LAND) {
00936       if (st->airport.tile == INVALID_TILE) {
00937         /* Airport has been removed, abort the landing procedure */
00938         v->state = FLYING;
00939         UpdateAircraftCache(v);
00940         AircraftNextAirportPos_and_Order(v);
00941         /* get aircraft back on running altitude */
00942         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00943         continue;
00944       }
00945 
00946       uint curz = GetSlopeZ(x + amd.x, y + amd.y) + 1;
00947 
00948       /* We're not flying below our destination, right? */
00949       assert(curz <= z);
00950       int t = max(1U, dist - 4);
00951       int delta = z - curz;
00952 
00953       /* Only start lowering when we're sufficiently close for a 1:1 glide */
00954       if (delta >= t) {
00955         z -= CeilDiv(z - curz, t);
00956       }
00957       if (z < curz) z = curz;
00958     }
00959 
00960     /* We've landed. Decrease speed when we're reaching end of runway. */
00961     if (amd.flag & AMED_BRAKE) {
00962       uint curz = GetSlopeZ(x, y) + 1;
00963 
00964       if (z > curz) {
00965         z--;
00966       } else if (z < curz) {
00967         z++;
00968       }
00969 
00970     }
00971 
00972     SetAircraftPosition(v, gp.x, gp.y, z);
00973   } while (--count != 0);
00974   return false;
00975 }
00976 
00981 static bool HandleCrashedAircraft(Aircraft *v)
00982 {
00983   v->crashed_counter += 3;
00984 
00985   Station *st = GetTargetAirportIfValid(v);
00986 
00987   /* make aircraft crash down to the ground */
00988   if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
00989     uint z = GetSlopeZ(v->x_pos, v->y_pos);
00990     v->z_pos -= 1;
00991     if (v->z_pos == z) {
00992       v->crashed_counter = 500;
00993       v->z_pos++;
00994     }
00995   }
00996 
00997   if (v->crashed_counter < 650) {
00998     uint32 r;
00999     if (Chance16R(1, 32, r)) {
01000       static const DirDiff delta[] = {
01001         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01002       };
01003 
01004       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01005       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01006       r = Random();
01007       CreateEffectVehicleRel(v,
01008         GB(r, 0, 4) - 4,
01009         GB(r, 4, 4) - 4,
01010         GB(r, 8, 4),
01011         EV_EXPLOSION_SMALL);
01012     }
01013   } else if (v->crashed_counter >= 10000) {
01014     /*  remove rubble of crashed airplane */
01015 
01016     /* clear runway-in on all airports, set by crashing plane
01017      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01018      * but they all share the same number */
01019     if (st != NULL) {
01020       CLRBITS(st->airport.flags, RUNWAY_IN_block);
01021       CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport
01022       CLRBITS(st->airport.flags, RUNWAY_IN2_block);    // intercontinental
01023     }
01024 
01025     delete v;
01026 
01027     return false;
01028   }
01029 
01030   return true;
01031 }
01032 
01033 
01034 static void HandleAircraftSmoke(Aircraft *v)
01035 {
01036   static const struct {
01037     int8 x;
01038     int8 y;
01039   } smoke_pos[] = {
01040     {  5,  5 },
01041     {  6,  0 },
01042     {  5, -5 },
01043     {  0, -6 },
01044     { -5, -5 },
01045     { -6,  0 },
01046     { -5,  5 },
01047     {  0,  6 }
01048   };
01049 
01050   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01051 
01052   if (v->cur_speed < 10) {
01053     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01054     v->breakdown_ctr = 0;
01055     return;
01056   }
01057 
01058   if ((v->tick_counter & 0x1F) == 0) {
01059     CreateEffectVehicleRel(v,
01060       smoke_pos[v->direction].x,
01061       smoke_pos[v->direction].y,
01062       2,
01063       EV_SMOKE
01064     );
01065   }
01066 }
01067 
01068 void HandleMissingAircraftOrders(Aircraft *v)
01069 {
01070   /*
01071    * We do not have an order. This can be divided into two cases:
01072    * 1) we are heading to an invalid station. In this case we must
01073    *    find another airport to go to. If there is nowhere to go,
01074    *    we will destroy the aircraft as it otherwise will enter
01075    *    the holding pattern for the first airport, which can cause
01076    *    the plane to go into an undefined state when building an
01077    *    airport with the same StationID.
01078    * 2) we are (still) heading to a (still) valid airport, then we
01079    *    can continue going there. This can happen when you are
01080    *    changing the aircraft's orders while in-flight or in for
01081    *    example a depot. However, when we have a current order to
01082    *    go to a depot, we have to keep that order so the aircraft
01083    *    actually stops.
01084    */
01085   const Station *st = GetTargetAirportIfValid(v);
01086   if (st == NULL) {
01087     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01088     CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01089     cur_company.Restore();
01090 
01091     if (ret.Failed()) CrashAirplane(v);
01092   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01093     v->current_order.Free();
01094   }
01095 }
01096 
01097 
01098 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01099 {
01100   /* Orders are changed in flight, ensure going to the right station. */
01101   if (this->state == FLYING) {
01102     AircraftNextAirportPos_and_Order(this);
01103   }
01104 
01105   /* Aircraft do not use dest-tile */
01106   return 0;
01107 }
01108 
01109 void Aircraft::MarkDirty()
01110 {
01111   this->UpdateViewport(false, false);
01112   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01113 }
01114 
01115 
01116 uint Aircraft::Crash(bool flooded)
01117 {
01118   uint pass = Vehicle::Crash(flooded) + 2; // pilots
01119   this->crashed_counter = flooded ? 9000 : 0; // max 10000, disappear pretty fast when flooded
01120 
01121   return pass;
01122 }
01123 
01128 static void CrashAirplane(Aircraft *v)
01129 {
01130   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01131 
01132   uint pass = v->Crash();
01133   SetDParam(0, pass);
01134 
01135   v->cargo.Truncate(0);
01136   v->Next()->cargo.Truncate(0);
01137   const Station *st = GetTargetAirportIfValid(v);
01138   StringID newsitem;
01139   if (st == NULL) {
01140     newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01141   } else {
01142     SetDParam(1, st->index);
01143     newsitem = STR_NEWS_AIRCRAFT_CRASH;
01144   }
01145 
01146   AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, st == NULL ? AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : AIEventVehicleCrashed::CRASH_PLANE_LANDING));
01147 
01148   AddVehicleNewsItem(newsitem,
01149     NS_ACCIDENT,
01150     v->index,
01151     st != NULL ? st->index : INVALID_STATION);
01152 
01153   ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01154   SndPlayVehicleFx(SND_12_EXPLOSION, v);
01155 }
01156 
01161 static void MaybeCrashAirplane(Aircraft *v)
01162 {
01163   if (_settings_game.vehicle.plane_crashes == 0) return;
01164 
01165   Station *st = Station::Get(v->targetairport);
01166 
01167   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01168   uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01169   if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01170       (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01171       !_cheats.no_jetcrash.value) {
01172     prob /= 20;
01173   } else {
01174     prob /= 1500;
01175   }
01176 
01177   if (GB(Random(), 0, 22) > prob) return;
01178 
01179   /* Crash the airplane. Remove all goods stored at the station. */
01180   for (CargoID i = 0; i < NUM_CARGO; i++) {
01181     st->goods[i].rating = 1;
01182     st->goods[i].cargo.Truncate(0);
01183   }
01184 
01185   CrashAirplane(v);
01186 }
01187 
01193 static void AircraftEntersTerminal(Aircraft *v)
01194 {
01195   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01196 
01197   Station *st = Station::Get(v->targetairport);
01198   v->last_station_visited = v->targetairport;
01199 
01200   /* Check if station was ever visited before */
01201   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01202     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01203     SetDParam(0, st->index);
01204     /* show newsitem of celebrating citizens */
01205     AddVehicleNewsItem(
01206       STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01207       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01208       v->index,
01209       st->index
01210     );
01211     AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01212   }
01213 
01214   v->BeginLoading();
01215 }
01216 
01221 static void AircraftLandAirplane(Aircraft *v)
01222 {
01223   v->UpdateDeltaXY(INVALID_DIR);
01224 
01225   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01226     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01227   }
01228 }
01229 
01230 
01232 void AircraftNextAirportPos_and_Order(Aircraft *v)
01233 {
01234   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01235     v->targetairport = v->current_order.GetDestination();
01236   }
01237 
01238   const Station *st = GetTargetAirportIfValid(v);
01239   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01240   Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01241   v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01242 }
01243 
01244 void AircraftLeaveHangar(Aircraft *v)
01245 {
01246   v->cur_speed = 0;
01247   v->subspeed = 0;
01248   v->progress = 0;
01249   v->direction = DIR_SE;
01250   v->vehstatus &= ~VS_HIDDEN;
01251   {
01252     Vehicle *u = v->Next();
01253     u->vehstatus &= ~VS_HIDDEN;
01254 
01255     /* Rotor blades */
01256     u = u->Next();
01257     if (u != NULL) {
01258       u->vehstatus &= ~VS_HIDDEN;
01259       u->cur_speed = 80;
01260     }
01261   }
01262 
01263   VehicleServiceInDepot(v);
01264   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01265   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01266   SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01267 }
01268 
01272 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01273 {
01274   AircraftEntersTerminal(v);
01275   v->state = apc->layout[v->pos].heading;
01276 }
01277 
01283 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01284 {
01285   VehicleEnterDepot(v);
01286   v->state = apc->layout[v->pos].heading;
01287 }
01288 
01294 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01295 {
01296   /* if we just arrived, execute EnterHangar first */
01297   if (v->previous_pos != v->pos) {
01298     AircraftEventHandler_EnterHangar(v, apc);
01299     return;
01300   }
01301 
01302   /* if we were sent to the depot, stay there */
01303   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01304     v->current_order.Free();
01305     return;
01306   }
01307 
01308   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01309       !v->current_order.IsType(OT_GOTO_DEPOT))
01310     return;
01311 
01312   /* if the block of the next position is busy, stay put */
01313   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01314 
01315   /* We are already at the target airport, we need to find a terminal */
01316   if (v->current_order.GetDestination() == v->targetairport) {
01317     /* FindFreeTerminal:
01318      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01319     if (v->subtype == AIR_HELICOPTER) {
01320       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01321     } else {
01322       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01323     }
01324   } else { // Else prepare for launch.
01325     /* airplane goto state takeoff, helicopter to helitakeoff */
01326     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01327   }
01328   AircraftLeaveHangar(v);
01329   AirportMove(v, apc);
01330 }
01331 
01333 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01334 {
01335   /* if we just arrived, execute EnterTerminal first */
01336   if (v->previous_pos != v->pos) {
01337     AircraftEventHandler_EnterTerminal(v, apc);
01338     /* on an airport with helipads, a helicopter will always land there
01339      * and get serviced at the same time - setting */
01340     if (_settings_game.order.serviceathelipad) {
01341       if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01342         /* an exerpt of ServiceAircraft, without the invisibility stuff */
01343         v->date_of_last_service = _date;
01344         v->breakdowns_since_last_service = 0;
01345         v->reliability = Engine::Get(v->engine_type)->reliability;
01346         SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01347       }
01348     }
01349     return;
01350   }
01351 
01352   if (v->current_order.IsType(OT_NOTHING)) return;
01353 
01354   /* if the block of the next position is busy, stay put */
01355   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01356 
01357   /* airport-road is free. We either have to go to another airport, or to the hangar
01358    * ---> start moving */
01359 
01360   bool go_to_hangar = false;
01361   switch (v->current_order.GetType()) {
01362     case OT_GOTO_STATION: // ready to fly to another airport
01363       break;
01364     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
01365       go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01366       break;
01367     case OT_CONDITIONAL:
01368       /* In case of a conditional order we just have to wait a tick
01369        * longer, so the conditional order can actually be processed;
01370        * we should not clear the order as that makes us go nowhere. */
01371       return;
01372     default:  // orders have been deleted (no orders), goto depot and don't bother us
01373       v->current_order.Free();
01374       go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01375   }
01376 
01377   if (go_to_hangar) {
01378     v->state = HANGAR;
01379   } else {
01380     /* airplane goto state takeoff, helicopter to helitakeoff */
01381     v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01382   }
01383   AirportMove(v, apc);
01384 }
01385 
01386 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01387 {
01388   error("OK, you shouldn't be here, check your Airport Scheme!");
01389 }
01390 
01391 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01392 {
01393   PlayAircraftSound(v); // play takeoffsound for airplanes
01394   v->state = STARTTAKEOFF;
01395 }
01396 
01397 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01398 {
01399   v->state = ENDTAKEOFF;
01400   v->UpdateDeltaXY(INVALID_DIR);
01401 }
01402 
01403 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01404 {
01405   v->state = FLYING;
01406   /* get the next position to go to, differs per airport */
01407   AircraftNextAirportPos_and_Order(v);
01408 }
01409 
01410 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01411 {
01412   v->state = FLYING;
01413   v->UpdateDeltaXY(INVALID_DIR);
01414 
01415   /* get the next position to go to, differs per airport */
01416   AircraftNextAirportPos_and_Order(v);
01417 
01418   /* Send the helicopter to a hangar if needed for replacement */
01419   if (v->NeedsAutomaticServicing()) {
01420     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01421     DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01422     cur_company.Restore();
01423   }
01424 }
01425 
01426 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01427 {
01428   Station *st = Station::Get(v->targetairport);
01429 
01430   /* runway busy or not allowed to use this airstation, circle */
01431   if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01432     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01433      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01434      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01435     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01436     const AirportFTA *current = apc->layout[v->pos].next;
01437     while (current != NULL) {
01438       if (current->heading == landingtype) {
01439         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01440          * we don't want that for plane in air
01441          * hack for speed thingie */
01442         uint16 tcur_speed = v->cur_speed;
01443         uint16 tsubspeed = v->subspeed;
01444         if (!AirportHasBlock(v, current, apc)) {
01445           v->state = landingtype; // LANDING / HELILANDING
01446           /* it's a bit dirty, but I need to set position to next position, otherwise
01447            * if there are multiple runways, plane won't know which one it took (because
01448            * they all have heading LANDING). And also occupy that block! */
01449           v->pos = current->next_position;
01450           SETBITS(st->airport.flags, apc->layout[v->pos].block);
01451           return;
01452         }
01453         v->cur_speed = tcur_speed;
01454         v->subspeed = tsubspeed;
01455       }
01456       current = current->next;
01457     }
01458   }
01459   v->state = FLYING;
01460   v->pos = apc->layout[v->pos].next_position;
01461 }
01462 
01463 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01464 {
01465   v->state = ENDLANDING;
01466   AircraftLandAirplane(v);  // maybe crash airplane
01467 
01468   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01469   if (v->NeedsAutomaticServicing()) {
01470     Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01471     DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01472     cur_company.Restore();
01473   }
01474 }
01475 
01476 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01477 {
01478   v->state = HELIENDLANDING;
01479   v->UpdateDeltaXY(INVALID_DIR);
01480 }
01481 
01482 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01483 {
01484   /* next block busy, don't do a thing, just wait */
01485   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01486 
01487   /* if going to terminal (OT_GOTO_STATION) choose one
01488    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01489    * 2. not going for terminal (but depot, no order),
01490    * --> get out of the way to the hangar. */
01491   if (v->current_order.IsType(OT_GOTO_STATION)) {
01492     if (AirportFindFreeTerminal(v, apc)) return;
01493   }
01494   v->state = HANGAR;
01495 
01496 }
01497 
01498 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01499 {
01500   /*  next block busy, don't do a thing, just wait */
01501   if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01502 
01503   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01504    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01505    * 2. not going for terminal (but depot, no order),
01506    * --> get out of the way to the hangar IF there are terminals on the airport.
01507    * --> else TAKEOFF
01508    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01509    * must go to a hangar. */
01510   if (v->current_order.IsType(OT_GOTO_STATION)) {
01511     if (AirportFindFreeHelipad(v, apc)) return;
01512   }
01513   v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01514 }
01515 
01521 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01523 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01524   AircraftEventHandler_General,        // TO_ALL         =  0
01525   AircraftEventHandler_InHangar,       // HANGAR         =  1
01526   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01527   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01528   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01529   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01530   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01531   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01532   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01533   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01534   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01535   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01536   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01537   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01538   AircraftEventHandler_Flying,         // FLYING         = 14
01539   AircraftEventHandler_Landing,        // LANDING        = 15
01540   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01541   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01542   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01543   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01544   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01545   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01546 };
01547 
01548 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01549 {
01550   /* we have left the previous block, and entered the new one. Free the previous block */
01551   if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01552     Station *st = Station::Get(v->targetairport);
01553 
01554     CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01555   }
01556 }
01557 
01558 static void AirportGoToNextPosition(Aircraft *v)
01559 {
01560   /* if aircraft is not in position, wait until it is */
01561   if (!AircraftController(v)) return;
01562 
01563   const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01564 
01565   AirportClearBlock(v, apc);
01566   AirportMove(v, apc); // move aircraft to next position
01567 }
01568 
01569 /* gets pos from vehicle and next orders */
01570 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01571 {
01572   /* error handling */
01573   if (v->pos >= apc->nofelements) {
01574     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01575     assert(v->pos < apc->nofelements);
01576   }
01577 
01578   const AirportFTA *current = &apc->layout[v->pos];
01579   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01580   if (current->heading == v->state) {
01581     byte prev_pos = v->pos; // location could be changed in state, so save it before-hand
01582     byte prev_state = v->state;
01583     _aircraft_state_handlers[v->state](v, apc);
01584     if (v->state != FLYING) v->previous_pos = prev_pos;
01585     if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01586     return true;
01587   }
01588 
01589   v->previous_pos = v->pos; // save previous location
01590 
01591   /* there is only one choice to move to */
01592   if (current->next == NULL) {
01593     if (AirportSetBlocks(v, current, apc)) {
01594       v->pos = current->next_position;
01595       UpdateAircraftCache(v);
01596     } // move to next position
01597     return false;
01598   }
01599 
01600   /* there are more choices to choose from, choose the one that
01601    * matches our heading */
01602   do {
01603     if (v->state == current->heading || current->heading == TO_ALL) {
01604       if (AirportSetBlocks(v, current, apc)) {
01605         v->pos = current->next_position;
01606         UpdateAircraftCache(v);
01607       } // move to next position
01608       return false;
01609     }
01610     current = current->next;
01611   } while (current != NULL);
01612 
01613   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01614   NOT_REACHED();
01615 }
01616 
01618 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01619 {
01620   const AirportFTA *reference = &apc->layout[v->pos];
01621   const AirportFTA *next = &apc->layout[current_pos->next_position];
01622 
01623   /* same block, then of course we can move */
01624   if (apc->layout[current_pos->position].block != next->block) {
01625     const Station *st = Station::Get(v->targetairport);
01626     uint64 airport_flags = next->block;
01627 
01628     /* check additional possible extra blocks */
01629     if (current_pos != reference && current_pos->block != NOTHING_block) {
01630       airport_flags |= current_pos->block;
01631     }
01632 
01633     if (st->airport.flags & airport_flags) {
01634       v->cur_speed = 0;
01635       v->subspeed = 0;
01636       return true;
01637     }
01638   }
01639   return false;
01640 }
01641 
01649 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01650 {
01651   const AirportFTA *next = &apc->layout[current_pos->next_position];
01652   const AirportFTA *reference = &apc->layout[v->pos];
01653 
01654   /* if the next position is in another block, check it and wait until it is free */
01655   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01656     uint64 airport_flags = next->block;
01657     /* search for all all elements in the list with the same state, and blocks != N
01658      * this means more blocks should be checked/set */
01659     const AirportFTA *current = current_pos;
01660     if (current == reference) current = current->next;
01661     while (current != NULL) {
01662       if (current->heading == current_pos->heading && current->block != 0) {
01663         airport_flags |= current->block;
01664         break;
01665       }
01666       current = current->next;
01667     };
01668 
01669     /* if the block to be checked is in the next position, then exclude that from
01670      * checking, because it has been set by the airplane before */
01671     if (current_pos->block == next->block) airport_flags ^= next->block;
01672 
01673     Station *st = Station::Get(v->targetairport);
01674     if (st->airport.flags & airport_flags) {
01675       v->cur_speed = 0;
01676       v->subspeed = 0;
01677       return false;
01678     }
01679 
01680     if (next->block != NOTHING_block) {
01681       SETBITS(st->airport.flags, airport_flags); // occupy next block
01682     }
01683   }
01684   return true;
01685 }
01686 
01691 struct MovementTerminalMapping {
01692   AirportMovementStates state; 
01693   uint64 airport_flag;         
01694 };
01695 
01697 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01698   {TERM1, TERM1_block},
01699   {TERM2, TERM2_block},
01700   {TERM3, TERM3_block},
01701   {TERM4, TERM4_block},
01702   {TERM5, TERM5_block},
01703   {TERM6, TERM6_block},
01704   {TERM7, TERM7_block},
01705   {TERM8, TERM8_block},
01706   {HELIPAD1, HELIPAD1_block},
01707   {HELIPAD2, HELIPAD2_block},
01708   {HELIPAD3, HELIPAD3_block},
01709 };
01710 
01718 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01719 {
01720   assert(last_terminal <= lengthof(_airport_terminal_mapping));
01721   Station *st = Station::Get(v->targetairport);
01722   for (; i < last_terminal; i++) {
01723     if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01724       /* TERMINAL# HELIPAD# */
01725       v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad
01726       SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad
01727       return true;
01728     }
01729   }
01730   return false;
01731 }
01732 
01738 static uint GetNumTerminals(const AirportFTAClass *apc)
01739 {
01740   uint num = 0;
01741 
01742   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01743 
01744   return num;
01745 }
01746 
01753 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01754 {
01755   /* example of more terminalgroups
01756    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01757    * Heading 255 denotes a group. We see 2 groups here:
01758    * 1. group 0 -- TERM_GROUP1_block (check block)
01759    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01760    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01761    * looks at the corresponding terminals of that group. If no free ones are found, other
01762    * possible groups are checked (in this case group 1, since that is after group 0). If that
01763    * fails, then attempt fails and plane waits
01764    */
01765   if (apc->terminals[0] > 1) {
01766     const Station *st = Station::Get(v->targetairport);
01767     const AirportFTA *temp = apc->layout[v->pos].next;
01768 
01769     while (temp != NULL) {
01770       if (temp->heading == 255) {
01771         if (!(st->airport.flags & temp->block)) {
01772           /* read which group do we want to go to?
01773            * (the first free group) */
01774           uint target_group = temp->next_position + 1;
01775 
01776           /* at what terminal does the group start?
01777            * that means, sum up all terminals of
01778            * groups with lower number */
01779           uint group_start = 0;
01780           for (uint i = 1; i < target_group; i++) {
01781             group_start += apc->terminals[i];
01782           }
01783 
01784           uint group_end = group_start + apc->terminals[target_group];
01785           if (FreeTerminal(v, group_start, group_end)) return true;
01786         }
01787       } else {
01788         /* once the heading isn't 255, we've exhausted the possible blocks.
01789          * So we cannot move */
01790         return false;
01791       }
01792       temp = temp->next;
01793     }
01794   }
01795 
01796   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01797   return FreeTerminal(v, 0, GetNumTerminals(apc));
01798 }
01799 
01806 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01807 {
01808   /* if an airport doesn't have helipads, use terminals */
01809   if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01810 
01811   /* only 1 helicoptergroup, check all helipads
01812    * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01813   return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01814 }
01815 
01816 static bool AircraftEventHandler(Aircraft *v, int loop)
01817 {
01818   v->tick_counter++;
01819 
01820   if (v->vehstatus & VS_CRASHED) {
01821     return HandleCrashedAircraft(v);
01822   }
01823 
01824   if (v->vehstatus & VS_STOPPED) return true;
01825 
01826   v->HandleBreakdown();
01827 
01828   HandleAircraftSmoke(v);
01829   ProcessOrders(v);
01830   v->HandleLoading(loop != 0);
01831 
01832   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01833 
01834   AirportGoToNextPosition(v);
01835 
01836   return true;
01837 }
01838 
01839 bool Aircraft::Tick()
01840 {
01841   if (!this->IsNormalAircraft()) return true;
01842 
01843   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01844 
01845   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01846 
01847   this->current_order_time++;
01848 
01849   for (uint i = 0; i != 2; i++) {
01850     /* stop if the aircraft was deleted */
01851     if (!AircraftEventHandler(this, i)) return false;
01852   }
01853 
01854   return true;
01855 }
01856 
01857 
01864 Station *GetTargetAirportIfValid(const Aircraft *v)
01865 {
01866   assert(v->type == VEH_AIRCRAFT);
01867 
01868   Station *st = Station::GetIfValid(v->targetairport);
01869   if (st == NULL) return NULL;
01870 
01871   return st->airport.tile == INVALID_TILE ? NULL : st;
01872 }
01873 
01878 void UpdateAirplanesOnNewStation(const Station *st)
01879 {
01880   /* only 1 station is updated per function call, so it is enough to get entry_point once */
01881   const AirportFTAClass *ap = st->airport.GetFTA();
01882   Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01883 
01884   Aircraft *v;
01885   FOR_ALL_AIRCRAFT(v) {
01886     if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01887     assert(v->state == FLYING);
01888     v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01889     UpdateAircraftCache(v);
01890   }
01891 }

Generated on Sun Jan 9 16:01:52 2011 for OpenTTD by  doxygen 1.6.1