aircraft_cmd.cpp

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

Generated on Fri May 27 04:19:39 2011 for OpenTTD by  doxygen 1.6.1