aircraft_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: aircraft_cmd.cpp 15718 2009-03-15 00:32:18Z rubidium $ */
00002 
00006 #include "stdafx.h"
00007 #include "aircraft.h"
00008 #include "debug.h"
00009 #include "landscape.h"
00010 #include "news_func.h"
00011 #include "vehicle_gui.h"
00012 #include "newgrf_engine.h"
00013 #include "newgrf_sound.h"
00014 #include "spritecache.h"
00015 #include "strings_func.h"
00016 #include "command_func.h"
00017 #include "window_func.h"
00018 #include "date_func.h"
00019 #include "vehicle_func.h"
00020 #include "sound_func.h"
00021 #include "functions.h"
00022 #include "variables.h"
00023 #include "cheat_type.h"
00024 #include "autoreplace_func.h"
00025 #include "autoreplace_gui.h"
00026 #include "gfx_func.h"
00027 #include "ai/ai.hpp"
00028 #include "company_func.h"
00029 #include "effectvehicle_func.h"
00030 #include "settings_type.h"
00031 
00032 #include "table/strings.h"
00033 #include "table/sprites.h"
00034 
00035 void Aircraft::UpdateDeltaXY(Direction direction)
00036 {
00037   uint32 x;
00038 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
00039   switch (this->subtype) {
00040     default: NOT_REACHED();
00041     case AIR_AIRCRAFT:
00042     case AIR_HELICOPTER:
00043       switch (this->u.air.state) {
00044         case ENDTAKEOFF:
00045         case LANDING:
00046         case HELILANDING:
00047         case FLYING:     x = MKIT(24, 24, -1, -1); break;
00048         default:         x = MKIT( 2,  2, -1, -1); break;
00049       }
00050       this->z_extent = 5;
00051       break;
00052     case AIR_SHADOW:     this->z_extent = 1; x = MKIT(2,  2,  0,  0); break;
00053     case AIR_ROTOR:      this->z_extent = 1; x = MKIT(2,  2, -1, -1); break;
00054   }
00055 #undef MKIT
00056 
00057   this->x_offs        = GB(x,  0, 8);
00058   this->y_offs        = GB(x,  8, 8);
00059   this->x_extent      = GB(x, 16, 8);
00060   this->y_extent      = GB(x, 24, 8);
00061 }
00062 
00063 
00066 static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
00067 static const byte _airport_terminal_flag[] =  {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
00068 
00069 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc);
00070 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00071 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00072 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc);
00073 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc);
00074 static void CrashAirplane(Vehicle *v);
00075 
00076 static const SpriteID _aircraft_sprite[] = {
00077   0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
00078   0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
00079   0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
00080   0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
00081   0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
00082   0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
00083   0x0EBD, 0x0EC5
00084 };
00085 
00087 enum HelicopterRotorStates {
00088   HRS_ROTOR_STOPPED,
00089   HRS_ROTOR_MOVING_1,
00090   HRS_ROTOR_MOVING_2,
00091   HRS_ROTOR_MOVING_3,
00092 };
00093 
00100 static StationID FindNearestHangar(const Vehicle *v)
00101 {
00102   const Station *st;
00103   uint best = 0;
00104   StationID index = INVALID_STATION;
00105   TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
00106 
00107   FOR_ALL_STATIONS(st) {
00108     if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00109 
00110     const AirportFTAClass *afc = st->Airport();
00111     if (afc->nof_depots == 0 || (
00112           /* don't crash the plane if we know it can't land at the airport */
00113           afc->flags & AirportFTAClass::SHORT_STRIP &&
00114           AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
00115           !_cheats.no_jetcrash.value
00116         )) {
00117       continue;
00118     }
00119 
00120     /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
00121     uint distance = DistanceSquare(vtile, st->airport_tile);
00122     if (distance < best || index == INVALID_STATION) {
00123       best = distance;
00124       index = st->index;
00125     }
00126   }
00127   return index;
00128 }
00129 
00130 #if 0
00131 
00134 static bool HaveHangarInOrderList(Vehicle *v)
00135 {
00136   const Order *order;
00137 
00138   FOR_VEHICLE_ORDERS(v, order) {
00139     const Station *st = GetStation(order->station);
00140     if (st->owner == v->owner && st->facilities & FACIL_AIRPORT) {
00141       /* If an airport doesn't have a hangar, skip it */
00142       if (st->Airport()->nof_depots != 0)
00143         return true;
00144     }
00145   }
00146 
00147   return false;
00148 }
00149 #endif
00150 
00151 SpriteID Aircraft::GetImage(Direction direction) const
00152 {
00153   uint8 spritenum = this->spritenum;
00154 
00155   if (is_custom_sprite(spritenum)) {
00156     SpriteID sprite = GetCustomVehicleSprite(this, direction);
00157     if (sprite != 0) return sprite;
00158 
00159     spritenum = GetEngine(this->engine_type)->image_index;
00160   }
00161 
00162   return direction + _aircraft_sprite[spritenum];
00163 }
00164 
00165 SpriteID GetRotorImage(const Vehicle *v)
00166 {
00167   assert(v->subtype == AIR_HELICOPTER);
00168 
00169   const Vehicle *w = v->Next()->Next();
00170   if (is_custom_sprite(v->spritenum)) {
00171     SpriteID sprite = GetCustomRotorSprite(v, false);
00172     if (sprite != 0) return sprite;
00173   }
00174 
00175   /* Return standard rotor sprites if there are no custom sprites for this helicopter */
00176   return SPR_ROTOR_STOPPED + w->u.air.state;
00177 }
00178 
00179 static SpriteID GetAircraftIcon(EngineID engine)
00180 {
00181   uint8 spritenum = AircraftVehInfo(engine)->image_index;
00182 
00183   if (is_custom_sprite(spritenum)) {
00184     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00185     if (sprite != 0) return sprite;
00186 
00187     spritenum = GetEngine(engine)->image_index;
00188   }
00189 
00190   return 6 + _aircraft_sprite[spritenum];
00191 }
00192 
00193 void DrawAircraftEngine(int x, int y, EngineID engine, SpriteID pal)
00194 {
00195   DrawSprite(GetAircraftIcon(engine), pal, 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, x, y - 5);
00201   }
00202 }
00203 
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 
00224 uint16 AircraftDefaultCargoCapacity(CargoID cid, const AircraftVehicleInfo *avi)
00225 {
00226   assert(cid != CT_INVALID);
00227 
00228   /* An aircraft can carry twice as much goods as normal cargo,
00229    * and four times as many passengers. */
00230   switch (cid) {
00231     case CT_PASSENGERS:
00232       return avi->passenger_capacity;
00233     case CT_MAIL:
00234       return avi->passenger_capacity + avi->mail_capacity;
00235     case CT_GOODS:
00236       return (avi->passenger_capacity + avi->mail_capacity) / 2;
00237     default:
00238       return (avi->passenger_capacity + avi->mail_capacity) / 4;
00239   }
00240 }
00241 
00249 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00250 {
00251   if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);
00252 
00253   const AircraftVehicleInfo *avi = AircraftVehInfo(p1);
00254   const Engine *e = GetEngine(p1);
00255   CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
00256 
00257   /* Engines without valid cargo should not be available */
00258   if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00259 
00260   /* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
00261   if (flags & DC_QUERY_COST) return value;
00262 
00263   if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00264 
00265   /* Prevent building aircraft types at places which can't handle them */
00266   if (!CanVehicleUseStation(p1, GetStationByTile(tile))) return CMD_ERROR;
00267 
00268   /* Allocate 2 or 3 vehicle structs, depending on type
00269    * vl[0] = aircraft, vl[1] = shadow, [vl[2] = rotor] */
00270   Vehicle *vl[3];
00271   if (!Vehicle::AllocateList(vl, avi->subtype & AIR_CTOL ? 2 : 3)) {
00272     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00273   }
00274 
00275   UnitID unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
00276   if (unit_num > _settings_game.vehicle.max_aircraft)
00277     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00278 
00279   if (flags & DC_EXEC) {
00280     Vehicle *v = vl[0]; // aircraft
00281     Vehicle *u = vl[1]; // shadow
00282 
00283     v = new (v) Aircraft();
00284     u = new (u) Aircraft();
00285     v->unitnumber = unit_num;
00286     v->direction = DIR_SE;
00287 
00288     v->owner = u->owner = _current_company;
00289 
00290     v->tile = tile;
00291 //    u->tile = 0;
00292 
00293     uint x = TileX(tile) * TILE_SIZE + 5;
00294     uint y = TileY(tile) * TILE_SIZE + 3;
00295 
00296     v->x_pos = u->x_pos = x;
00297     v->y_pos = u->y_pos = y;
00298 
00299     u->z_pos = GetSlopeZ(x, y);
00300     v->z_pos = u->z_pos + 1;
00301 
00302     v->running_ticks = 0;
00303 
00304 //    u->delta_x = u->delta_y = 0;
00305 
00306     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00307     u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00308 
00309     v->spritenum = avi->image_index;
00310 //    v->cargo_count = u->number_of_pieces = 0;
00311 
00312     v->cargo_cap = avi->passenger_capacity;
00313     u->cargo_cap = avi->mail_capacity;
00314 
00315     v->cargo_type = e->GetDefaultCargoType();
00316     u->cargo_type = CT_MAIL;
00317 
00318     v->cargo_subtype = 0;
00319 
00320     v->name = NULL;
00321 //    v->next_order_param = v->next_order = 0;
00322 
00323 //    v->load_unload_time_rem = 0;
00324 //    v->progress = 0;
00325     v->last_station_visited = INVALID_STATION;
00326 //    v->destination_coords = 0;
00327 
00328     v->max_speed = avi->max_speed;
00329     v->acceleration = avi->acceleration;
00330     v->engine_type = p1;
00331     u->engine_type = p1;
00332 
00333     v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00334     v->UpdateDeltaXY(INVALID_DIR);
00335     v->value = value.GetCost();
00336 
00337     u->subtype = AIR_SHADOW;
00338     u->UpdateDeltaXY(INVALID_DIR);
00339 
00340     if (v->cargo_type != CT_PASSENGERS) {
00341       uint16 callback = CALLBACK_FAILED;
00342 
00343       if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00344         callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00345       }
00346 
00347       if (callback == CALLBACK_FAILED) {
00348         /* Callback failed, or not executed; use the default cargo capacity */
00349         v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
00350       } else {
00351         v->cargo_cap = callback;
00352       }
00353 
00354       /* Set the 'second compartent' capacity to none */
00355       u->cargo_cap = 0;
00356     }
00357 
00358     v->reliability = e->reliability;
00359     v->reliability_spd_dec = e->reliability_spd_dec;
00360     v->max_age = e->lifelength * DAYS_IN_LEAP_YEAR;
00361 
00362     _new_vehicle_id = v->index;
00363 
00364     /* When we click on hangar we know the tile it is on. By that we know
00365      * its position in the array of depots the airport has.....we can search
00366      * layout for #th position of depot. Since layout must start with a listing
00367      * of all depots, it is simple */
00368     for (uint i = 0;; i++) {
00369       const Station *st = GetStationByTile(tile);
00370       const AirportFTAClass *apc = st->Airport();
00371 
00372       assert(i != apc->nof_depots);
00373       if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
00374         assert(apc->layout[i].heading == HANGAR);
00375         v->u.air.pos = apc->layout[i].position;
00376         break;
00377       }
00378     }
00379 
00380     v->u.air.state = HANGAR;
00381     v->u.air.previous_pos = v->u.air.pos;
00382     v->u.air.targetairport = GetStationIndex(tile);
00383     v->SetNext(u);
00384 
00385     v->service_interval = _settings_game.vehicle.servint_aircraft;
00386 
00387     v->date_of_last_service = _date;
00388     v->build_year = u->build_year = _cur_year;
00389 
00390     v->cur_image = u->cur_image = 0xEA0;
00391 
00392     v->random_bits = VehicleRandomBits();
00393     u->random_bits = VehicleRandomBits();
00394 
00395     v->vehicle_flags = 0;
00396     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00397 
00398     UpdateAircraftCache(v);
00399 
00400     VehicleMove(v, false);
00401     VehicleMove(u, false);
00402 
00403     /* Aircraft with 3 vehicles (chopper)? */
00404     if (v->subtype == AIR_HELICOPTER) {
00405       Vehicle *w = vl[2];
00406 
00407       w = new (w) Aircraft();
00408       w->engine_type = p1;
00409       w->direction = DIR_N;
00410       w->owner = _current_company;
00411       w->x_pos = v->x_pos;
00412       w->y_pos = v->y_pos;
00413       w->z_pos = v->z_pos + 5;
00414       w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00415       w->spritenum = 0xFF;
00416       w->subtype = AIR_ROTOR;
00417       w->cur_image = SPR_ROTOR_STOPPED;
00418       w->random_bits = VehicleRandomBits();
00419       /* Use rotor's air.state to store the rotor animation frame */
00420       w->u.air.state = HRS_ROTOR_STOPPED;
00421       w->UpdateDeltaXY(INVALID_DIR);
00422 
00423       u->SetNext(w);
00424       VehicleMove(w, false);
00425     }
00426 
00427     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00428     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00429     InvalidateWindow(WC_COMPANY, v->owner);
00430     if (IsLocalCompany())
00431       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Aircraft window
00432 
00433     GetCompany(_current_company)->num_engines[p1]++;
00434   }
00435 
00436   return value;
00437 }
00438 
00439 
00447 CommandCost CmdSellAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00448 {
00449   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00450 
00451   Vehicle *v = GetVehicle(p1);
00452 
00453   if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00454   if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00455 
00456   if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
00457 
00458   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00459 
00460   if (flags & DC_EXEC) {
00461     delete v;
00462   }
00463 
00464   return ret;
00465 }
00466 
00467 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00468 {
00469   const Station *st = GetTargetAirportIfValid(this);
00470   /* If the station is not a valid airport or if it has no hangars */
00471   if (st == NULL || st->Airport()->nof_depots == 0) {
00472     /* the aircraft has to search for a hangar on its own */
00473     StationID station = FindNearestHangar(this);
00474 
00475     if (station == INVALID_STATION) return false;
00476 
00477     st = GetStation(station);
00478   }
00479 
00480   if (location    != NULL) *location    = st->xy;
00481   if (destination != NULL) *destination = st->index;
00482 
00483   return true;
00484 }
00485 
00495 CommandCost CmdSendAircraftToHangar(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00496 {
00497   if (p2 & DEPOT_MASS_SEND) {
00498     /* Mass goto depot requested */
00499     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00500     return SendAllVehiclesToDepot(VEH_AIRCRAFT, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00501   }
00502 
00503   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00504 
00505   Vehicle *v = GetVehicle(p1);
00506 
00507   if (v->type != VEH_AIRCRAFT) return CMD_ERROR;
00508 
00509   return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00510 }
00511 
00512 
00523 CommandCost CmdRefitAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00524 {
00525   byte new_subtype = GB(p2, 8, 8);
00526 
00527   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00528 
00529   Vehicle *v = GetVehicle(p1);
00530 
00531   if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00532   if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00533   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
00534 
00535   /* Check cargo */
00536   CargoID new_cid = GB(p2, 0, 8);
00537   if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
00538 
00539   /* Check the refit capacity callback */
00540   uint16 callback = CALLBACK_FAILED;
00541   if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00542     /* Back up the existing cargo type */
00543     CargoID temp_cid = v->cargo_type;
00544     byte temp_subtype = v->cargo_subtype;
00545     v->cargo_type = new_cid;
00546     v->cargo_subtype = new_subtype;
00547 
00548     callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00549 
00550     /* Restore the cargo type */
00551     v->cargo_type = temp_cid;
00552     v->cargo_subtype = temp_subtype;
00553   }
00554 
00555   const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00556 
00557   uint pass;
00558   if (callback == CALLBACK_FAILED) {
00559     /* If the callback failed, or wasn't executed, use the aircraft's
00560      * default cargo capacity */
00561     pass = AircraftDefaultCargoCapacity(new_cid, avi);
00562   } else {
00563     pass = callback;
00564   }
00565   _returned_refit_capacity = pass;
00566 
00567   CommandCost cost;
00568   if (new_cid != v->cargo_type) {
00569     cost = GetRefitCost(v->engine_type);
00570   }
00571 
00572   if (flags & DC_EXEC) {
00573     v->cargo_cap = pass;
00574 
00575     Vehicle *u = v->Next();
00576     uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
00577     u->cargo_cap = mail;
00578     v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
00579     u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
00580     v->cargo_type = new_cid;
00581     v->cargo_subtype = new_subtype;
00582     v->colourmap = PAL_NONE; // invalidate vehicle colour map
00583     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00584     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00585     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00586   }
00587 
00588   return cost;
00589 }
00590 
00591 
00592 static void CheckIfAircraftNeedsService(Vehicle *v)
00593 {
00594   if (_settings_game.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00595   if (v->IsInDepot()) {
00596     VehicleServiceInDepot(v);
00597     return;
00598   }
00599 
00600   const Station *st = GetStation(v->current_order.GetDestination());
00601   /* only goto depot if the target airport has terminals (eg. it is airport) */
00602   if (st->IsValid() && st->airport_tile != INVALID_TILE && st->Airport()->terminals != NULL) {
00603 //    printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
00604 //    v->u.air.targetairport = st->index;
00605     v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00606     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00607   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00608     v->current_order.MakeDummy();
00609     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00610   }
00611 }
00612 
00613 Money Aircraft::GetRunningCost() const
00614 {
00615   return GetVehicleProperty(this, 0x0E, AircraftVehInfo(this->engine_type)->running_cost) * _price.aircraft_running;
00616 }
00617 
00618 void Aircraft::OnNewDay()
00619 {
00620   if (!IsNormalAircraft(this)) return;
00621 
00622   if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00623 
00624   CheckOrders(this);
00625 
00626   CheckVehicleBreakdown(this);
00627   AgeVehicle(this);
00628   CheckIfAircraftNeedsService(this);
00629 
00630   if (this->running_ticks == 0) return;
00631 
00632   CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00633 
00634   this->profit_this_year -= cost.GetCost();
00635   this->running_ticks = 0;
00636 
00637   SubtractMoneyFromCompanyFract(this->owner, cost);
00638 
00639   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
00640   InvalidateWindowClasses(WC_AIRCRAFT_LIST);
00641 }
00642 
00643 static void AgeAircraftCargo(Vehicle *v)
00644 {
00645   if (_age_cargo_skip_counter != 0) return;
00646 
00647   do {
00648     v->cargo.AgeCargo();
00649     v = v->Next();
00650   } while (v != NULL);
00651 }
00652 
00653 static void HelicopterTickHandler(Vehicle *v)
00654 {
00655   Vehicle *u = v->Next()->Next();
00656 
00657   if (u->vehstatus & VS_HIDDEN) return;
00658 
00659   /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
00660    * loading/unloading at a terminal or stopped */
00661   if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00662     if (u->cur_speed != 0) {
00663       u->cur_speed++;
00664       if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) {
00665         u->cur_speed = 0;
00666       }
00667     }
00668   } else {
00669     if (u->cur_speed == 0)
00670       u->cur_speed = 0x70;
00671 
00672     if (u->cur_speed >= 0x50)
00673       u->cur_speed--;
00674   }
00675 
00676   int tick = ++u->tick_counter;
00677   int spd = u->cur_speed >> 4;
00678 
00679   SpriteID img;
00680   if (spd == 0) {
00681     u->u.air.state = HRS_ROTOR_STOPPED;
00682     img = GetRotorImage(v);
00683     if (u->cur_image == img) return;
00684   } else if (tick >= spd) {
00685     u->tick_counter = 0;
00686     u->u.air.state++;
00687     if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1;
00688     img = GetRotorImage(v);
00689   } else {
00690     return;
00691   }
00692 
00693   u->cur_image = img;
00694 
00695   VehicleMove(u, true);
00696 }
00697 
00698 void SetAircraftPosition(Vehicle *v, int x, int y, int z)
00699 {
00700   v->x_pos = x;
00701   v->y_pos = y;
00702   v->z_pos = z;
00703 
00704   v->cur_image = v->GetImage(v->direction);
00705   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00706 
00707   VehicleMove(v, true);
00708 
00709   Vehicle *u = v->Next();
00710 
00711   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00712   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00713   u->x_pos = x;
00714   u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
00715 
00716   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00717   u->z_pos = GetSlopeZ(safe_x, safe_y);
00718   u->cur_image = v->cur_image;
00719 
00720   VehicleMove(u, true);
00721 
00722   u = u->Next();
00723   if (u != NULL) {
00724     u->x_pos = x;
00725     u->y_pos = y;
00726     u->z_pos = z + 5;
00727 
00728     VehicleMove(u, true);
00729   }
00730 }
00731 
00735 void HandleAircraftEnterHangar(Vehicle *v)
00736 {
00737   v->subspeed = 0;
00738   v->progress = 0;
00739 
00740   Vehicle *u = v->Next();
00741   u->vehstatus |= VS_HIDDEN;
00742   u = u->Next();
00743   if (u != NULL) {
00744     u->vehstatus |= VS_HIDDEN;
00745     u->cur_speed = 0;
00746   }
00747 
00748   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00749 }
00750 
00751 static void PlayAircraftSound(const Vehicle *v)
00752 {
00753   if (!PlayVehicleSound(v, VSE_START)) {
00754     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00755   }
00756 }
00757 
00758 
00759 void UpdateAircraftCache(Vehicle *v)
00760 {
00761   uint max_speed = GetVehicleProperty(v, 0x0C, 0);
00762   if (max_speed != 0) {
00763     /* Convert from original units to (approx) km/h */
00764     max_speed = (max_speed * 129) / 10;
00765 
00766     v->u.air.cached_max_speed = max_speed;
00767   } else {
00768     v->u.air.cached_max_speed = 0xFFFF;
00769   }
00770 }
00771 
00772 
00776 enum AircraftSpeedLimits {
00777   SPEED_LIMIT_TAXI     =     50,  
00778   SPEED_LIMIT_APPROACH =    230,  
00779   SPEED_LIMIT_BROKEN   =    320,  
00780   SPEED_LIMIT_HOLD     =    425,  
00781   SPEED_LIMIT_NONE     = 0xFFFF   
00782 };
00783 
00791 static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00792 {
00793   uint spd = v->acceleration * 16;
00794   byte t;
00795 
00796   /* Adjust speed limits by plane speed factor to prevent taxiing
00797    * and take-off speeds being too low. */
00798   speed_limit *= _settings_game.vehicle.plane_speed;
00799 
00800   if (v->u.air.cached_max_speed < speed_limit) {
00801     if (v->cur_speed < speed_limit) hard_limit = false;
00802     speed_limit = v->u.air.cached_max_speed;
00803   }
00804 
00805   speed_limit = min(speed_limit, v->max_speed);
00806 
00807   v->subspeed = (t=v->subspeed) + (byte)spd;
00808 
00809   /* Aircraft's current speed is used twice so that very fast planes are
00810    * forced to slow down rapidly in the short distance needed. The magic
00811    * value 16384 was determined to give similar results to the old speed/48
00812    * method at slower speeds. This also results in less reduction at slow
00813    * speeds to that aircraft do not get to taxi speed straight after
00814    * touchdown. */
00815   if (!hard_limit && v->cur_speed > speed_limit) {
00816     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00817   }
00818 
00819   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00820 
00821   /* adjust speed for broken vehicles */
00822   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00823 
00824   /* updates statusbar only if speed have changed to save CPU time */
00825   if (spd != v->cur_speed) {
00826     v->cur_speed = spd;
00827     if (_settings_client.gui.vehicle_speed)
00828       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00829   }
00830 
00831   /* Adjust distance moved by plane speed setting */
00832   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00833 
00834   if (!(v->direction & 1)) spd = spd * 3 / 4;
00835 
00836   spd += v->progress;
00837   v->progress = (byte)spd;
00838   return spd >> 8;
00839 }
00840 
00848 byte GetAircraftFlyingAltitude(const Vehicle *v)
00849 {
00850   /* Make sure Aircraft fly no lower so that they don't conduct
00851    * CFITs (controlled flight into terrain)
00852    */
00853   byte base_altitude = 150;
00854 
00855   /* Make sure eastbound and westbound planes do not "crash" into each
00856    * other by providing them with vertical seperation
00857    */
00858   switch (v->direction) {
00859     case DIR_N:
00860     case DIR_NE:
00861     case DIR_E:
00862     case DIR_SE:
00863       base_altitude += 10;
00864       break;
00865 
00866     default: break;
00867   }
00868 
00869   /* Make faster planes fly higher so that they can overtake slower ones */
00870   base_altitude += min(20 * (v->max_speed / 200), 90);
00871 
00872   return base_altitude;
00873 }
00874 
00888 static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
00889 {
00890   assert(v != NULL);
00891   assert(apc != NULL);
00892 
00893   /* In the case the station doesn't exit anymore, set target tile 0.
00894    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00895    * or it will simply crash in next tick */
00896   TileIndex tile = 0;
00897 
00898   if (IsValidStationID(v->u.air.targetairport)) {
00899     const Station *st = GetStation(v->u.air.targetairport);
00900     /* Make sure we don't go to INVALID_TILE if the airport has been removed. */
00901     tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00902   }
00903 
00904   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00905   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00906 
00907   DiagDirection dir;
00908   if (abs(delta_y) < abs(delta_x)) {
00909     /* We are northeast or southwest of the airport */
00910     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00911   } else {
00912     /* We are northwest or southeast of the airport */
00913     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00914   }
00915   return apc->entry_points[dir];
00916 }
00917 
00925 static bool AircraftController(Vehicle *v)
00926 {
00927   int count;
00928 
00929   /* NULL if station is invalid */
00930   const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00931   /* INVALID_TILE if there is no station */
00932   TileIndex tile = INVALID_TILE;
00933   if (st != NULL) {
00934     tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00935   }
00936   /* DUMMY if there is no station or no airport */
00937   const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00938 
00939   /* prevent going to INVALID_TILE if airport is deleted. */
00940   if (st == NULL || st->airport_tile == INVALID_TILE) {
00941     /* Jump into our "holding pattern" state machine if possible */
00942     if (v->u.air.pos >= afc->nofelements) {
00943       v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
00944     } else if (v->u.air.targetairport != v->current_order.GetDestination()) {
00945       /* If not possible, just get out of here fast */
00946       v->u.air.state = FLYING;
00947       UpdateAircraftCache(v);
00948       AircraftNextAirportPos_and_Order(v);
00949       /* get aircraft back on running altitude */
00950       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00951       return false;
00952     }
00953   }
00954 
00955   /*  get airport moving data */
00956   const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
00957 
00958   int x = TileX(tile) * TILE_SIZE;
00959   int y = TileY(tile) * TILE_SIZE;
00960 
00961   /* Helicopter raise */
00962   if (amd->flag & AMED_HELI_RAISE) {
00963     Vehicle *u = v->Next()->Next();
00964 
00965     /* Make sure the rotors don't rotate too fast */
00966     if (u->cur_speed > 32) {
00967       v->cur_speed = 0;
00968       if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00969     } else {
00970       u->cur_speed = 32;
00971       count = UpdateAircraftSpeed(v);
00972       if (count > 0) {
00973         v->tile = 0;
00974 
00975         /* Reached altitude? */
00976         if (v->z_pos >= 184) {
00977           v->cur_speed = 0;
00978           return true;
00979         }
00980         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00981       }
00982     }
00983     return false;
00984   }
00985 
00986   /* Helicopter landing. */
00987   if (amd->flag & AMED_HELI_LOWER) {
00988     if (st == NULL) {
00989       /* FIXME - AircraftController -> if station no longer exists, do not land
00990        * helicopter will circle until sign disappears, then go to next order
00991        * what to do when it is the only order left, right now it just stays in 1 place */
00992       v->u.air.state = FLYING;
00993       UpdateAircraftCache(v);
00994       AircraftNextAirportPos_and_Order(v);
00995       return false;
00996     }
00997 
00998     /* Vehicle is now at the airport. */
00999     v->tile = tile;
01000 
01001     /* Find altitude of landing position. */
01002     int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
01003 
01004     if (z == v->z_pos) {
01005       Vehicle *u = v->Next()->Next();
01006 
01007       /*  Increase speed of rotors. When speed is 80, we've landed. */
01008       if (u->cur_speed >= 80) return true;
01009       u->cur_speed += 4;
01010     } else {
01011       count = UpdateAircraftSpeed(v);
01012       if (count > 0) {
01013         if (v->z_pos > z) {
01014           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
01015         } else {
01016           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
01017         }
01018       }
01019     }
01020     return false;
01021   }
01022 
01023   /* Get distance from destination pos to current pos. */
01024   uint dist = abs(x + amd->x - v->x_pos) +  abs(y + amd->y - v->y_pos);
01025 
01026   /* Need exact position? */
01027   if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01028 
01029   /* At final pos? */
01030   if (dist == 0) {
01031     /* Change direction smoothly to final direction. */
01032     DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01033     /* if distance is 0, and plane points in right direction, no point in calling
01034      * UpdateAircraftSpeed(). So do it only afterwards */
01035     if (dirdiff == DIRDIFF_SAME) {
01036       v->cur_speed = 0;
01037       return true;
01038     }
01039 
01040     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
01041 
01042     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01043     v->cur_speed >>= 1;
01044 
01045     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01046     return false;
01047   }
01048 
01049   uint speed_limit = SPEED_LIMIT_TAXI;
01050   bool hard_limit = true;
01051 
01052   if (amd->flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
01053   if (amd->flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
01054   if (amd->flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
01055   if (amd->flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
01056 
01057   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
01058   if (count == 0) return false;
01059 
01060   if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
01061 
01062   do {
01063 
01064     GetNewVehiclePosResult gp;
01065 
01066     if (dist < 4 || amd->flag & AMED_LAND) {
01067       /* move vehicle one pixel towards target */
01068       gp.x = (v->x_pos != (x + amd->x)) ?
01069           v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
01070           v->x_pos;
01071       gp.y = (v->y_pos != (y + amd->y)) ?
01072           v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01073           v->y_pos;
01074 
01075       /* Oilrigs must keep v->tile as st->airport_tile, since the landing pad is in a non-airport tile */
01076       gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01077 
01078     } else {
01079 
01080       /* Turn. Do it slowly if in the air. */
01081       Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01082       if (newdir != v->direction) {
01083         v->direction = newdir;
01084         if (amd->flag & AMED_SLOWTURN) {
01085           if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
01086         } else {
01087           v->cur_speed >>= 1;
01088         }
01089       }
01090 
01091       /* Move vehicle. */
01092       gp = GetNewVehiclePos(v);
01093     }
01094 
01095     v->tile = gp.new_tile;
01096     /* If vehicle is in the air, use tile coordinate 0. */
01097     if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01098 
01099     /* Adjust Z for land or takeoff? */
01100     uint z = v->z_pos;
01101 
01102     if (amd->flag & AMED_TAKEOFF) {
01103       z = min(z + 2, GetAircraftFlyingAltitude(v));
01104     }
01105 
01106     if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01107 
01108     if (amd->flag & AMED_LAND) {
01109       if (st->airport_tile == INVALID_TILE) {
01110         /* Airport has been removed, abort the landing procedure */
01111         v->u.air.state = FLYING;
01112         UpdateAircraftCache(v);
01113         AircraftNextAirportPos_and_Order(v);
01114         /* get aircraft back on running altitude */
01115         SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01116         continue;
01117       }
01118 
01119       uint curz = GetSlopeZ(x, y) + 1;
01120 
01121       if (curz > z) {
01122         z++;
01123       } else {
01124         int t = max(1U, dist - 4);
01125 
01126         z -= ((z - curz) + t - 1) / t;
01127         if (z < curz) z = curz;
01128       }
01129     }
01130 
01131     /* We've landed. Decrase speed when we're reaching end of runway. */
01132     if (amd->flag & AMED_BRAKE) {
01133       uint curz = GetSlopeZ(x, y) + 1;
01134 
01135       if (z > curz) {
01136         z--;
01137       } else if (z < curz) {
01138         z++;
01139       }
01140 
01141     }
01142 
01143     SetAircraftPosition(v, gp.x, gp.y, z);
01144   } while (--count != 0);
01145   return false;
01146 }
01147 
01148 
01149 static void HandleCrashedAircraft(Vehicle *v)
01150 {
01151   v->u.air.crashed_counter += 3;
01152 
01153   Station *st = GetTargetAirportIfValid(v);
01154 
01155   /* make aircraft crash down to the ground */
01156   if (v->u.air.crashed_counter < 500 && st == NULL && ((v->u.air.crashed_counter % 3) == 0) ) {
01157     uint z = GetSlopeZ(v->x_pos, v->y_pos);
01158     v->z_pos -= 1;
01159     if (v->z_pos == z) {
01160       v->u.air.crashed_counter = 500;
01161       v->z_pos++;
01162     }
01163   }
01164 
01165   if (v->u.air.crashed_counter < 650) {
01166     uint32 r;
01167     if (Chance16R(1,32,r)) {
01168       static const DirDiff delta[] = {
01169         DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01170       };
01171 
01172       v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01173       SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01174       r = Random();
01175       CreateEffectVehicleRel(v,
01176         GB(r, 0, 4) - 4,
01177         GB(r, 4, 4) - 4,
01178         GB(r, 8, 4),
01179         EV_EXPLOSION_SMALL);
01180     }
01181   } else if (v->u.air.crashed_counter >= 10000) {
01182     /*  remove rubble of crashed airplane */
01183 
01184     /* clear runway-in on all airports, set by crashing plane
01185      * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc.
01186      * but they all share the same number */
01187     if (st != NULL) {
01188       CLRBITS(st->airport_flags, RUNWAY_IN_block);
01189       CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block); // commuter airport
01190       CLRBITS(st->airport_flags, RUNWAY_IN2_block);    // intercontinental
01191     }
01192 
01193     delete v;
01194   }
01195 }
01196 
01197 static void HandleBrokenAircraft(Vehicle *v)
01198 {
01199   if (v->breakdown_ctr != 1) {
01200     v->breakdown_ctr = 1;
01201     v->vehstatus |= VS_AIRCRAFT_BROKEN;
01202 
01203     if (v->breakdowns_since_last_service != 255)
01204       v->breakdowns_since_last_service++;
01205     InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01206     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01207   }
01208 }
01209 
01210 
01211 static void HandleAircraftSmoke(Vehicle *v)
01212 {
01213   static const struct {
01214     int8 x;
01215     int8 y;
01216   } smoke_pos[] = {
01217     {  5,  5 },
01218     {  6,  0 },
01219     {  5, -5 },
01220     {  0, -6 },
01221     { -5, -5 },
01222     { -6,  0 },
01223     { -5,  5 },
01224     {  0,  6 }
01225   };
01226 
01227   if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01228 
01229   if (v->cur_speed < 10) {
01230     v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01231     v->breakdown_ctr = 0;
01232     return;
01233   }
01234 
01235   if ((v->tick_counter & 0x1F) == 0) {
01236     CreateEffectVehicleRel(v,
01237       smoke_pos[v->direction].x,
01238       smoke_pos[v->direction].y,
01239       2,
01240       EV_SMOKE
01241     );
01242   }
01243 }
01244 
01245 void HandleMissingAircraftOrders(Vehicle *v)
01246 {
01247   /*
01248    * We do not have an order. This can be divided into two cases:
01249    * 1) we are heading to an invalid station. In this case we must
01250    *    find another airport to go to. If there is nowhere to go,
01251    *    we will destroy the aircraft as it otherwise will enter
01252    *    the holding pattern for the first airport, which can cause
01253    *    the plane to go into an undefined state when building an
01254    *    airport with the same StationID.
01255    * 2) we are (still) heading to a (still) valid airport, then we
01256    *    can continue going there. This can happen when you are
01257    *    changing the aircraft's orders while in-flight or in for
01258    *    example a depot. However, when we have a current order to
01259    *    go to a depot, we have to keep that order so the aircraft
01260    *    actually stops.
01261    */
01262   const Station *st = GetTargetAirportIfValid(v);
01263   if (st == NULL) {
01264     CommandCost ret;
01265     CompanyID old_company = _current_company;
01266 
01267     _current_company = v->owner;
01268     ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01269     _current_company = old_company;
01270 
01271     if (CmdFailed(ret)) CrashAirplane(v);
01272   } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01273     v->current_order.Free();
01274   }
01275 }
01276 
01277 
01278 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01279 {
01280   /* Orders are changed in flight, ensure going to the right station. */
01281   if (this->u.air.state == FLYING) {
01282     AircraftNextAirportPos_and_Order(this);
01283   }
01284 
01285   /* Aircraft do not use dest-tile */
01286   return 0;
01287 }
01288 
01289 void Aircraft::MarkDirty()
01290 {
01291   this->cur_image = this->GetImage(this->direction);
01292   if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01293   MarkSingleVehicleDirty(this);
01294 }
01295 
01296 static void CrashAirplane(Vehicle *v)
01297 {
01298   v->vehstatus |= VS_CRASHED;
01299   v->u.air.crashed_counter = 0;
01300 
01301   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01302 
01303   InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01304 
01305   uint amt = 2;
01306   if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count();
01307   SetDParam(0, amt);
01308 
01309   v->cargo.Truncate(0);
01310   v->Next()->cargo.Truncate(0);
01311   const Station *st = GetTargetAirportIfValid(v);
01312   StringID newsitem;
01313   AIEventVehicleCrashed::CrashReason crash_reason;
01314   if (st == NULL) {
01315     newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
01316     crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
01317   } else {
01318     SetDParam(1, st->index);
01319     newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
01320     crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
01321   }
01322 
01323   AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
01324 
01325   AddNewsItem(newsitem,
01326     NS_ACCIDENT_VEHICLE,
01327     v->index,
01328     0);
01329 
01330   SndPlayVehicleFx(SND_12_EXPLOSION, v);
01331 }
01332 
01333 static void MaybeCrashAirplane(Vehicle *v)
01334 {
01335   Station *st = GetStation(v->u.air.targetairport);
01336 
01337   /* FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports */
01338   uint16 prob = 0x10000 / 1500;
01339   if (st->Airport()->flags & AirportFTAClass::SHORT_STRIP &&
01340       AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
01341       !_cheats.no_jetcrash.value) {
01342     prob = 0x10000 / 20;
01343   }
01344 
01345   if (GB(Random(), 0, 16) > prob) return;
01346 
01347   /* Crash the airplane. Remove all goods stored at the station. */
01348   for (CargoID i = 0; i < NUM_CARGO; i++) {
01349     st->goods[i].rating = 1;
01350     st->goods[i].cargo.Truncate(0);
01351   }
01352 
01353   CrashAirplane(v);
01354 }
01355 
01357 static void AircraftEntersTerminal(Vehicle *v)
01358 {
01359   if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01360 
01361   Station *st = GetStation(v->u.air.targetairport);
01362   v->last_station_visited = v->u.air.targetairport;
01363 
01364   /* Check if station was ever visited before */
01365   if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01366     st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01367     SetDParam(0, st->index);
01368     /* show newsitem of celebrating citizens */
01369     AddNewsItem(
01370       STR_A033_CITIZENS_CELEBRATE_FIRST,
01371       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01372       v->index,
01373       st->index
01374     );
01375     AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01376   }
01377 
01378   v->BeginLoading();
01379 }
01380 
01381 static void AircraftLandAirplane(Vehicle *v)
01382 {
01383   v->UpdateDeltaXY(INVALID_DIR);
01384 
01385   if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01386     SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01387   }
01388   MaybeCrashAirplane(v);
01389 }
01390 
01391 
01393 void AircraftNextAirportPos_and_Order(Vehicle *v)
01394 {
01395   if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01396     v->u.air.targetairport = v->current_order.GetDestination();
01397   }
01398 
01399   const Station *st = GetTargetAirportIfValid(v);
01400   const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01401   v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, apc);
01402 }
01403 
01404 void AircraftLeaveHangar(Vehicle *v)
01405 {
01406   v->cur_speed = 0;
01407   v->subspeed = 0;
01408   v->progress = 0;
01409   v->direction = DIR_SE;
01410   v->vehstatus &= ~VS_HIDDEN;
01411   {
01412     Vehicle *u = v->Next();
01413     u->vehstatus &= ~VS_HIDDEN;
01414 
01415     /* Rotor blades */
01416     u = u->Next();
01417     if (u != NULL) {
01418       u->vehstatus &= ~VS_HIDDEN;
01419       u->cur_speed = 80;
01420     }
01421   }
01422 
01423   VehicleServiceInDepot(v);
01424   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01425   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01426   InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01427 }
01428 
01433 static inline bool CheckSendAircraftToHangarForReplacement(const Vehicle *v)
01434 {
01435   EngineID new_engine;
01436   Company *c = GetCompany(v->owner);
01437 
01438   if (VehicleHasDepotOrders(v)) return false; // The aircraft will end up in the hangar eventually on it's own
01439 
01440   new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
01441 
01442   if (new_engine == INVALID_ENGINE) {
01443     /* There is no autoreplace assigned to this EngineID so we will set it to renew to the same type if needed */
01444     new_engine = v->engine_type;
01445 
01446     if (!v->NeedsAutorenewing(c)) {
01447       /* No need to replace the aircraft */
01448       return false;
01449     }
01450   }
01451 
01452   if (!HasBit(GetEngine(new_engine)->company_avail, v->owner)) {
01453     /* Engine is not buildable anymore */
01454     return false;
01455   }
01456 
01457   if (c->money < (c->engine_renew_money + (2 * DoCommand(0, new_engine, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT).GetCost()))) {
01458     /* We lack enough money to request the replacement right away.
01459      * We want 2*(the price of the new vehicle) and not looking at the value of the vehicle we are going to sell.
01460      * The reason is that we don't want to send a whole lot of vehicles to the hangars when we only have enough money to replace a single one.
01461      * Remember this happens in the background so the user can't stop this. */
01462     return false;
01463   }
01464 
01465   /* We found no reason NOT to send the aircraft to a hangar so we will send it there at once */
01466   return true;
01467 }
01468 
01472 static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
01473 {
01474   AircraftEntersTerminal(v);
01475   v->u.air.state = apc->layout[v->u.air.pos].heading;
01476 }
01477 
01478 static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
01479 {
01480   VehicleEnterDepot(v);
01481   v->u.air.state = apc->layout[v->u.air.pos].heading;
01482 }
01483 
01485 static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
01486 {
01487   /* if we just arrived, execute EnterHangar first */
01488   if (v->u.air.previous_pos != v->u.air.pos) {
01489     AircraftEventHandler_EnterHangar(v, apc);
01490     return;
01491   }
01492 
01493   /* if we were sent to the depot, stay there */
01494   if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01495     v->current_order.Free();
01496     return;
01497   }
01498 
01499   if (!v->current_order.IsType(OT_GOTO_STATION) &&
01500       !v->current_order.IsType(OT_GOTO_DEPOT))
01501     return;
01502 
01503   /* if the block of the next position is busy, stay put */
01504   if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01505 
01506   /* We are already at the target airport, we need to find a terminal */
01507   if (v->current_order.GetDestination() == v->u.air.targetairport) {
01508     /* FindFreeTerminal:
01509      * 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
01510     if (v->subtype == AIR_HELICOPTER) {
01511       if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
01512     } else {
01513       if (!AirportFindFreeTerminal(v, apc)) return; // airplane
01514     }
01515   } else { // Else prepare for launch.
01516     /* airplane goto state takeoff, helicopter to helitakeoff */
01517     v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01518   }
01519   AircraftLeaveHangar(v);
01520   AirportMove(v, apc);
01521 }
01522 
01524 static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
01525 {
01526   /* if we just arrived, execute EnterTerminal first */
01527   if (v->u.air.previous_pos != v->u.air.pos) {
01528     AircraftEventHandler_EnterTerminal(v, apc);
01529     /* on an airport with helipads, a helicopter will always land there
01530      * and get serviced at the same time - setting */
01531     if (_settings_game.order.serviceathelipad) {
01532       if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01533         /* an exerpt of ServiceAircraft, without the invisibility stuff */
01534         v->date_of_last_service = _date;
01535         v->breakdowns_since_last_service = 0;
01536         v->reliability = GetEngine(v->engine_type)->reliability;
01537         InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01538       }
01539     }
01540     return;
01541   }
01542 
01543   if (!v->current_order.IsValid()) return;
01544 
01545   /* if the block of the next position is busy, stay put */
01546   if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01547 
01548   /* airport-road is free. We either have to go to another airport, or to the hangar
01549    * ---> start moving */
01550 
01551   bool go_to_hangar = false;
01552   switch (v->current_order.GetType()) {
01553     case OT_GOTO_STATION: // ready to fly to another airport
01554       break;
01555     case OT_GOTO_DEPOT:   // visit hangar for serivicing, sale, etc.
01556       go_to_hangar = v->current_order.GetDestination() == v->u.air.targetairport;
01557       break;
01558     case OT_CONDITIONAL:
01559       /* In case of a conditional order we just have to wait a tick
01560        * longer, so the conditional order can actually be processed;
01561        * we should not clear the order as that makes us go nowhere. */
01562       return;
01563     default:  // orders have been deleted (no orders), goto depot and don't bother us
01564       v->current_order.Free();
01565       go_to_hangar = GetStation(v->u.air.targetairport)->Airport()->nof_depots != 0;
01566   }
01567 
01568   if (go_to_hangar) {
01569     v->u.air.state = HANGAR;
01570   } else {
01571     /* airplane goto state takeoff, helicopter to helitakeoff */
01572     v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01573   }
01574   AirportMove(v, apc);
01575 }
01576 
01577 static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
01578 {
01579   assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
01580 }
01581 
01582 static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc)
01583 {
01584   PlayAircraftSound(v); // play takeoffsound for airplanes
01585   v->u.air.state = STARTTAKEOFF;
01586 }
01587 
01588 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
01589 {
01590   v->u.air.state = ENDTAKEOFF;
01591   v->UpdateDeltaXY(INVALID_DIR);
01592 }
01593 
01594 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
01595 {
01596   v->u.air.state = FLYING;
01597   /* get the next position to go to, differs per airport */
01598   AircraftNextAirportPos_and_Order(v);
01599 }
01600 
01601 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
01602 {
01603   v->u.air.state = FLYING;
01604   v->UpdateDeltaXY(INVALID_DIR);
01605 
01606   /* get the next position to go to, differs per airport */
01607   AircraftNextAirportPos_and_Order(v);
01608 
01609   /* Send the helicopter to a hangar if needed for replacement */
01610   if (CheckSendAircraftToHangarForReplacement(v)) {
01611     _current_company = v->owner;
01612     DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01613     _current_company = OWNER_NONE;
01614   }
01615 }
01616 
01617 static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
01618 {
01619   Station *st = GetStation(v->u.air.targetairport);
01620 
01621   /* runway busy or not allowed to use this airstation, circle */
01622   if (apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES) &&
01623       st->airport_tile != INVALID_TILE &&
01624       (st->owner == OWNER_NONE || st->owner == v->owner)) {
01625     /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
01626      * if it is an airplane, look for LANDING, for helicopter HELILANDING
01627      * it is possible to choose from multiple landing runways, so loop until a free one is found */
01628     byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01629     const AirportFTA *current = apc->layout[v->u.air.pos].next;
01630     while (current != NULL) {
01631       if (current->heading == landingtype) {
01632         /* save speed before, since if AirportHasBlock is false, it resets them to 0
01633          * we don't want that for plane in air
01634          * hack for speed thingie */
01635         uint16 tcur_speed = v->cur_speed;
01636         uint16 tsubspeed = v->subspeed;
01637         if (!AirportHasBlock(v, current, apc)) {
01638           v->u.air.state = landingtype; // LANDING / HELILANDING
01639           /* it's a bit dirty, but I need to set position to next position, otherwise
01640            * if there are multiple runways, plane won't know which one it took (because
01641            * they all have heading LANDING). And also occupy that block! */
01642           v->u.air.pos = current->next_position;
01643           SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
01644           return;
01645         }
01646         v->cur_speed = tcur_speed;
01647         v->subspeed = tsubspeed;
01648       }
01649       current = current->next;
01650     }
01651   }
01652   v->u.air.state = FLYING;
01653   v->u.air.pos = apc->layout[v->u.air.pos].next_position;
01654 }
01655 
01656 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
01657 {
01658   v->u.air.state = ENDLANDING;
01659   AircraftLandAirplane(v);  // maybe crash airplane
01660 
01661   /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */
01662   if (CheckSendAircraftToHangarForReplacement(v)) {
01663     _current_company = v->owner;
01664     DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01665     _current_company = OWNER_NONE;
01666   }
01667 }
01668 
01669 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
01670 {
01671   v->u.air.state = HELIENDLANDING;
01672   v->UpdateDeltaXY(INVALID_DIR);
01673 }
01674 
01675 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
01676 {
01677   /* next block busy, don't do a thing, just wait */
01678   if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01679 
01680   /* if going to terminal (OT_GOTO_STATION) choose one
01681    * 1. in case all terminals are busy AirportFindFreeTerminal() returns false or
01682    * 2. not going for terminal (but depot, no order),
01683    * --> get out of the way to the hangar. */
01684   if (v->current_order.IsType(OT_GOTO_STATION)) {
01685     if (AirportFindFreeTerminal(v, apc)) return;
01686   }
01687   v->u.air.state = HANGAR;
01688 
01689 }
01690 
01691 static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
01692 {
01693   /*  next block busy, don't do a thing, just wait */
01694   if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01695 
01696   /* if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal
01697    * 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or
01698    * 2. not going for terminal (but depot, no order),
01699    * --> get out of the way to the hangar IF there are terminals on the airport.
01700    * --> else TAKEOFF
01701    * the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes
01702    * must go to a hangar. */
01703   if (v->current_order.IsType(OT_GOTO_STATION)) {
01704     if (AirportFindFreeHelipad(v, apc)) return;
01705   }
01706   v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01707 }
01708 
01709 typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
01710 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01711   AircraftEventHandler_General,        // TO_ALL         =  0
01712   AircraftEventHandler_InHangar,       // HANGAR         =  1
01713   AircraftEventHandler_AtTerminal,     // TERM1          =  2
01714   AircraftEventHandler_AtTerminal,     // TERM2          =  3
01715   AircraftEventHandler_AtTerminal,     // TERM3          =  4
01716   AircraftEventHandler_AtTerminal,     // TERM4          =  5
01717   AircraftEventHandler_AtTerminal,     // TERM5          =  6
01718   AircraftEventHandler_AtTerminal,     // TERM6          =  7
01719   AircraftEventHandler_AtTerminal,     // HELIPAD1       =  8
01720   AircraftEventHandler_AtTerminal,     // HELIPAD2       =  9
01721   AircraftEventHandler_TakeOff,        // TAKEOFF        = 10
01722   AircraftEventHandler_StartTakeOff,   // STARTTAKEOFF   = 11
01723   AircraftEventHandler_EndTakeOff,     // ENDTAKEOFF     = 12
01724   AircraftEventHandler_HeliTakeOff,    // HELITAKEOFF    = 13
01725   AircraftEventHandler_Flying,         // FLYING         = 14
01726   AircraftEventHandler_Landing,        // LANDING        = 15
01727   AircraftEventHandler_EndLanding,     // ENDLANDING     = 16
01728   AircraftEventHandler_HeliLanding,    // HELILANDING    = 17
01729   AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18
01730   AircraftEventHandler_AtTerminal,     // TERM7          = 19
01731   AircraftEventHandler_AtTerminal,     // TERM8          = 20
01732   AircraftEventHandler_AtTerminal,     // HELIPAD3       = 21
01733   AircraftEventHandler_AtTerminal,     // HELIPAD4       = 22
01734 };
01735 
01736 static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
01737 {
01738   /* we have left the previous block, and entered the new one. Free the previous block */
01739   if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
01740     Station *st = GetStation(v->u.air.targetairport);
01741 
01742     CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
01743   }
01744 }
01745 
01746 static void AirportGoToNextPosition(Vehicle *v)
01747 {
01748   /* if aircraft is not in position, wait until it is */
01749   if (!AircraftController(v)) return;
01750 
01751   const AirportFTAClass *apc = GetStation(v->u.air.targetairport)->Airport();
01752 
01753   AirportClearBlock(v, apc);
01754   AirportMove(v, apc); // move aircraft to next position
01755 }
01756 
01757 /* gets pos from vehicle and next orders */
01758 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
01759 {
01760   /* error handling */
01761   if (v->u.air.pos >= apc->nofelements) {
01762     DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
01763     assert(v->u.air.pos < apc->nofelements);
01764   }
01765 
01766   const AirportFTA *current = &apc->layout[v->u.air.pos];
01767   /* we have arrived in an important state (eg terminal, hangar, etc.) */
01768   if (current->heading == v->u.air.state) {
01769     byte prev_pos = v->u.air.pos; // location could be changed in state, so save it before-hand
01770     byte prev_state = v->u.air.state;
01771     _aircraft_state_handlers[v->u.air.state](v, apc);
01772     if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
01773     if (v->u.air.state != prev_state || v->u.air.pos != prev_pos) UpdateAircraftCache(v);
01774     return true;
01775   }
01776 
01777   v->u.air.previous_pos = v->u.air.pos; // save previous location
01778 
01779   /* there is only one choice to move to */
01780   if (current->next == NULL) {
01781     if (AirportSetBlocks(v, current, apc)) {
01782       v->u.air.pos = current->next_position;
01783       UpdateAircraftCache(v);
01784     } // move to next position
01785     return false;
01786   }
01787 
01788   /* there are more choices to choose from, choose the one that
01789    * matches our heading */
01790   do {
01791     if (v->u.air.state == current->heading || current->heading == TO_ALL) {
01792       if (AirportSetBlocks(v, current, apc)) {
01793         v->u.air.pos = current->next_position;
01794         UpdateAircraftCache(v);
01795       } // move to next position
01796       return false;
01797     }
01798     current = current->next;
01799   } while (current != NULL);
01800 
01801   DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->u.air.pos, v->u.air.state, v->index);
01802   assert(0);
01803   return false;
01804 }
01805 
01806 /*  returns true if the road ahead is busy, eg. you must wait before proceeding */
01807 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01808 {
01809   const AirportFTA *reference = &apc->layout[v->u.air.pos];
01810   const AirportFTA *next = &apc->layout[current_pos->next_position];
01811 
01812   /* same block, then of course we can move */
01813   if (apc->layout[current_pos->position].block != next->block) {
01814     const Station *st = GetStation(v->u.air.targetairport);
01815     uint64 airport_flags = next->block;
01816 
01817     /* check additional possible extra blocks */
01818     if (current_pos != reference && current_pos->block != NOTHING_block) {
01819       airport_flags |= current_pos->block;
01820     }
01821 
01822     if (HASBITS(st->airport_flags, airport_flags)) {
01823       v->cur_speed = 0;
01824       v->subspeed = 0;
01825       return true;
01826     }
01827   }
01828   return false;
01829 }
01830 
01838 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01839 {
01840   const AirportFTA *next = &apc->layout[current_pos->next_position];
01841   const AirportFTA *reference = &apc->layout[v->u.air.pos];
01842 
01843   /* if the next position is in another block, check it and wait until it is free */
01844   if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01845     uint64 airport_flags = next->block;
01846     /* search for all all elements in the list with the same state, and blocks != N
01847      * this means more blocks should be checked/set */
01848     const AirportFTA *current = current_pos;
01849     if (current == reference) current = current->next;
01850     while (current != NULL) {
01851       if (current->heading == current_pos->heading && current->block != 0) {
01852         airport_flags |= current->block;
01853         break;
01854       }
01855       current = current->next;
01856     };
01857 
01858     /* if the block to be checked is in the next position, then exclude that from
01859      * checking, because it has been set by the airplane before */
01860     if (current_pos->block == next->block) airport_flags ^= next->block;
01861 
01862     Station *st = GetStation(v->u.air.targetairport);
01863     if (HASBITS(st->airport_flags, airport_flags)) {
01864       v->cur_speed = 0;
01865       v->subspeed = 0;
01866       return false;
01867     }
01868 
01869     if (next->block != NOTHING_block) {
01870       SETBITS(st->airport_flags, airport_flags); // occupy next block
01871     }
01872   }
01873   return true;
01874 }
01875 
01876 static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
01877 {
01878   Station *st = GetStation(v->u.air.targetairport);
01879   for (; i < last_terminal; i++) {
01880     if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01881       /* TERMINAL# HELIPAD# */
01882       v->u.air.state = _airport_terminal_state[i]; // start moving to that terminal/helipad
01883       SetBit(st->airport_flags, _airport_terminal_flag[i]); // occupy terminal/helipad
01884       return true;
01885     }
01886   }
01887   return false;
01888 }
01889 
01890 static uint GetNumTerminals(const AirportFTAClass *apc)
01891 {
01892   uint num = 0;
01893 
01894   for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01895 
01896   return num;
01897 }
01898 
01899 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
01900 {
01901   /* example of more terminalgroups
01902    * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1},
01903    * Heading 255 denotes a group. We see 2 groups here:
01904    * 1. group 0 -- TERM_GROUP1_block (check block)
01905    * 2. group 1 -- TERM_GROUP2_ENTER_block (check block)
01906    * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it
01907    * looks at the corresponding terminals of that group. If no free ones are found, other
01908    * possible groups are checked (in this case group 1, since that is after group 0). If that
01909    * fails, then attempt fails and plane waits
01910    */
01911   if (apc->terminals[0] > 1) {
01912     const Station *st = GetStation(v->u.air.targetairport);
01913     const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01914 
01915     while (temp != NULL) {
01916       if (temp->heading == 255) {
01917         if (!HASBITS(st->airport_flags, temp->block)) {
01918           /* read which group do we want to go to?
01919            * (the first free group) */
01920           uint target_group = temp->next_position + 1;
01921 
01922           /* at what terminal does the group start?
01923            * that means, sum up all terminals of
01924            * groups with lower number */
01925           uint group_start = 0;
01926           for (uint i = 1; i < target_group; i++) {
01927             group_start += apc->terminals[i];
01928           }
01929 
01930           uint group_end = group_start + apc->terminals[target_group];
01931           if (FreeTerminal(v, group_start, group_end)) return true;
01932         }
01933       } else {
01934         /* once the heading isn't 255, we've exhausted the possible blocks.
01935          * So we cannot move */
01936         return false;
01937       }
01938       temp = temp->next;
01939     }
01940   }
01941 
01942   /* if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) */
01943   return FreeTerminal(v, 0, GetNumTerminals(apc));
01944 }
01945 
01946 static uint GetNumHelipads(const AirportFTAClass *apc)
01947 {
01948   uint num = 0;
01949 
01950   for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01951 
01952   return num;
01953 }
01954 
01955 
01956 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
01957 {
01958   /* if an airport doesn't have helipads, use terminals */
01959   if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01960 
01961   /* if there are more helicoptergroups, pick one, just as in AirportFindFreeTerminal() */
01962   if (apc->helipads[0] > 1) {
01963     const Station *st = GetStation(v->u.air.targetairport);
01964     const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01965 
01966     while (temp != NULL) {
01967       if (temp->heading == 255) {
01968         if (!HASBITS(st->airport_flags, temp->block)) {
01969 
01970           /* read which group do we want to go to?
01971            * (the first free group) */
01972           uint target_group = temp->next_position + 1;
01973 
01974           /* at what terminal does the group start?
01975            * that means, sum up all terminals of
01976            * groups with lower number */
01977           uint group_start = 0;
01978           for (uint i = 1; i < target_group; i++) {
01979             group_start += apc->helipads[i];
01980           }
01981 
01982           uint group_end = group_start + apc->helipads[target_group];
01983           if (FreeTerminal(v, group_start, group_end)) return true;
01984         }
01985       } else {
01986         /* once the heading isn't 255, we've exhausted the possible blocks.
01987          * So we cannot move */
01988         return false;
01989       }
01990       temp = temp->next;
01991     }
01992   } else {
01993     /* only 1 helicoptergroup, check all helipads
01994      * The blocks for helipads start after the last terminal (MAX_TERMINALS) */
01995     return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01996   }
01997   return false; // it shouldn't get here anytime, but just to be sure
01998 }
01999 
02000 static void AircraftEventHandler(Vehicle *v, int loop)
02001 {
02002   v->tick_counter++;
02003 
02004   if (v->vehstatus & VS_CRASHED) {
02005     HandleCrashedAircraft(v);
02006     return;
02007   }
02008 
02009   if (v->vehstatus & VS_STOPPED) return;
02010 
02011   /* aircraft is broken down? */
02012   if (v->breakdown_ctr != 0) {
02013     if (v->breakdown_ctr <= 2) {
02014       HandleBrokenAircraft(v);
02015     } else {
02016       if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
02017     }
02018   }
02019 
02020   HandleAircraftSmoke(v);
02021   ProcessOrders(v);
02022   v->HandleLoading(loop != 0);
02023 
02024   if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return;
02025 
02026   AirportGoToNextPosition(v);
02027 }
02028 
02029 void Aircraft::Tick()
02030 {
02031   if (!IsNormalAircraft(this)) return;
02032 
02033   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
02034 
02035   if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
02036 
02037   AgeAircraftCargo(this);
02038 
02039   this->current_order_time++;
02040 
02041   for (uint i = 0; i != 2; i++) {
02042     AircraftEventHandler(this, i);
02043     if (this->type != VEH_AIRCRAFT) // In case it was deleted
02044       break;
02045   }
02046 }
02047 
02048 
02054 Station *GetTargetAirportIfValid(const Vehicle *v)
02055 {
02056   assert(v->type == VEH_AIRCRAFT);
02057 
02058   StationID sid = v->u.air.targetairport;
02059 
02060   if (!IsValidStationID(sid)) return NULL;
02061 
02062   Station *st = GetStation(sid);
02063 
02064   return st->airport_tile == INVALID_TILE ? NULL : st;
02065 }
02066 
02071 void UpdateAirplanesOnNewStation(const Station *st)
02072 {
02073   /* only 1 station is updated per function call, so it is enough to get entry_point once */
02074   const AirportFTAClass *ap = st->Airport();
02075 
02076   Vehicle *v;
02077   FOR_ALL_VEHICLES(v) {
02078     if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
02079       if (v->u.air.targetairport == st->index) { // if heading to this airport
02080         /* update position of airplane. If plane is not flying, landing, or taking off
02081          * you cannot delete airport, so it doesn't matter */
02082         if (v->u.air.state >= FLYING) { // circle around
02083           v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, ap);
02084           v->u.air.state = FLYING;
02085           UpdateAircraftCache(v);
02086           /* landing plane needs to be reset to flying height (only if in pause mode upgrade,
02087            * in normal mode, plane is reset in AircraftController. It doesn't hurt for FLYING */
02088           GetNewVehiclePosResult gp = GetNewVehiclePos(v);
02089           /* set new position x,y,z */
02090           SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02091         } else {
02092           assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF);
02093           byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02094           /* search in airportdata for that heading
02095            * easiest to do, since this doesn't happen a lot */
02096           for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02097             if (ap->layout[cnt].heading == takeofftype) {
02098               v->u.air.pos = ap->layout[cnt].position;
02099               UpdateAircraftCache(v);
02100               break;
02101             }
02102           }
02103         }
02104       }
02105     }
02106   }
02107 }

Generated on Wed Apr 1 14:38:05 2009 for OpenTTD by  doxygen 1.5.6