aircraft_cmd.cpp

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

Generated on Thu Jan 20 22:57:31 2011 for OpenTTD by  doxygen 1.6.1