aircraft_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: aircraft_cmd.cpp 14422 2008-09-30 20:51:04Z rubidium $ */
00002 
00006 #include "stdafx.h"
00007 #include "openttd.h"
00008 #include "aircraft.h"
00009 #include "debug.h"
00010 #include "landscape.h"
00011 #include "station_map.h"
00012 #include "timetable.h"
00013 #include "depot_func.h"
00014 #include "news_func.h"
00015 #include "aircraft.h"
00016 #include "airport.h"
00017 #include "vehicle_gui.h"
00018 #include "newgrf_engine.h"
00019 #include "newgrf_callbacks.h"
00020 #include "newgrf_text.h"
00021 #include "newgrf_sound.h"
00022 #include "spritecache.h"
00023 #include "cargotype.h"
00024 #include "strings_func.h"
00025 #include "command_func.h"
00026 #include "window_func.h"
00027 #include "date_func.h"
00028 #include "vehicle_func.h"
00029 #include "sound_func.h"
00030 #include "functions.h"
00031 #include "variables.h"
00032 #include "cheat_func.h"
00033 #include "autoreplace_func.h"
00034 #include "autoreplace_gui.h"
00035 #include "gfx_func.h"
00036 #include "company_func.h"
00037 #include "settings_type.h"
00038 #include "order_func.h"
00039 #include "effectvehicle_func.h"
00040 
00041 #include "table/strings.h"
00042 #include "table/sprites.h"
00043 
00044 void Aircraft::UpdateDeltaXY(Direction direction)
00045 {
00046   uint32 x;
00047 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
00048   switch (this->subtype) {
00049     default: NOT_REACHED();
00050     case AIR_AIRCRAFT:
00051     case AIR_HELICOPTER:
00052       switch (this->u.air.state) {
00053         case ENDTAKEOFF:
00054         case LANDING:
00055         case HELILANDING:
00056         case FLYING:     x = MKIT(24, 24, -1, -1); break;
00057         default:         x = MKIT( 2,  2, -1, -1); break;
00058       }
00059       this->z_extent = 5;
00060       break;
00061     case AIR_SHADOW:     this->z_extent = 1; x = MKIT(2,  2,  0,  0); break;
00062     case AIR_ROTOR:      this->z_extent = 1; x = MKIT(2,  2, -1, -1); break;
00063   }
00064 #undef MKIT
00065 
00066   this->x_offs        = GB(x,  0, 8);
00067   this->y_offs        = GB(x,  8, 8);
00068   this->x_extent      = GB(x, 16, 8);
00069   this->y_extent      = GB(x, 24, 8);
00070 }
00071 
00072 
00075 static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
00076 static const byte _airport_terminal_flag[] =  {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
00077 
00078 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc);
00079 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00080 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00081 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc);
00082 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc);
00083 static void CrashAirplane(Vehicle *v);
00084 
00085 void AircraftNextAirportPos_and_Order(Vehicle *v);
00086 static byte GetAircraftFlyingAltitude(const Vehicle *v);
00087 
00088 static const SpriteID _aircraft_sprite[] = {
00089   0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
00090   0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
00091   0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
00092   0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
00093   0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
00094   0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
00095   0x0EBD, 0x0EC5
00096 };
00097 
00099 enum HelicopterRotorStates {
00100   HRS_ROTOR_STOPPED,
00101   HRS_ROTOR_MOVING_1,
00102   HRS_ROTOR_MOVING_2,
00103   HRS_ROTOR_MOVING_3,
00104 };
00105 
00112 static StationID FindNearestHangar(const Vehicle *v)
00113 {
00114   const Station *st;
00115   uint best = 0;
00116   StationID index = INVALID_STATION;
00117   TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
00118 
00119   FOR_ALL_STATIONS(st) {
00120     if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00121 
00122     const AirportFTAClass *afc = st->Airport();
00123     if (afc->nof_depots == 0 || (
00124           /* don't crash the plane if we know it can't land at the airport */
00125           afc->flags & AirportFTAClass::SHORT_STRIP &&
00126           AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
00127           !_cheats.no_jetcrash.value
00128         )) {
00129       continue;
00130     }
00131 
00132     /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */
00133     uint distance = DistanceSquare(vtile, st->airport_tile);
00134     if (distance < best || index == INVALID_STATION) {
00135       best = distance;
00136       index = st->index;
00137     }
00138   }
00139   return index;
00140 }
00141 
00142 #if 0
00143 
00146 static bool HaveHangarInOrderList(Vehicle *v)
00147 {
00148   const Order *order;
00149 
00150   FOR_VEHICLE_ORDERS(v, order) {
00151     const Station *st = GetStation(order->station);
00152     if (st->owner == v->owner && st->facilities & FACIL_AIRPORT) {
00153       /* If an airport doesn't have a hangar, skip it */
00154       if (st->Airport()->nof_depots != 0)
00155         return true;
00156     }
00157   }
00158 
00159   return false;
00160 }
00161 #endif
00162 
00163 SpriteID Aircraft::GetImage(Direction direction) const
00164 {
00165   uint8 spritenum = this->spritenum;
00166 
00167   if (is_custom_sprite(spritenum)) {
00168     SpriteID sprite = GetCustomVehicleSprite(this, direction);
00169     if (sprite != 0) return sprite;
00170 
00171     spritenum = GetEngine(this->engine_type)->image_index;
00172   }
00173 
00174   return direction + _aircraft_sprite[spritenum];
00175 }
00176 
00177 SpriteID GetRotorImage(const Vehicle *v)
00178 {
00179   assert(v->subtype == AIR_HELICOPTER);
00180 
00181   const Vehicle *w = v->Next()->Next();
00182   if (is_custom_sprite(v->spritenum)) {
00183     SpriteID sprite = GetCustomRotorSprite(v, false);
00184     if (sprite != 0) return sprite;
00185   }
00186 
00187   /* Return standard rotor sprites if there are no custom sprites for this helicopter */
00188   return SPR_ROTOR_STOPPED + w->u.air.state;
00189 }
00190 
00191 static SpriteID GetAircraftIcon(EngineID engine)
00192 {
00193   uint8 spritenum = AircraftVehInfo(engine)->image_index;
00194 
00195   if (is_custom_sprite(spritenum)) {
00196     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00197     if (sprite != 0) return sprite;
00198 
00199     spritenum = GetEngine(engine)->image_index;
00200   }
00201 
00202   return 6 + _aircraft_sprite[spritenum];
00203 }
00204 
00205 void DrawAircraftEngine(int x, int y, EngineID engine, SpriteID pal)
00206 {
00207   DrawSprite(GetAircraftIcon(engine), pal, x, y);
00208 
00209   if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00210     SpriteID rotor_sprite = GetCustomRotorIcon(engine);
00211     if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00212     DrawSprite(rotor_sprite, PAL_NONE, x, y - 5);
00213   }
00214 }
00215 
00221 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
00222 {
00223   const Sprite *spr = GetSprite(GetAircraftIcon(engine), ST_NORMAL);
00224 
00225   width  = spr->width;
00226   height = spr->height;
00227 }
00228 
00229 static CommandCost EstimateAircraftCost(EngineID engine, const AircraftVehicleInfo *avi)
00230 {
00231   return CommandCost(EXPENSES_NEW_VEHICLES, GetEngineProperty(engine, 0x0B, avi->cost_factor) * (_price.aircraft_base >> 3) >> 5);
00232 }
00233 
00234 
00242 uint16 AircraftDefaultCargoCapacity(CargoID cid, const AircraftVehicleInfo *avi)
00243 {
00244   assert(cid != CT_INVALID);
00245 
00246   /* An aircraft can carry twice as much goods as normal cargo,
00247    * and four times as many passengers. */
00248   switch (cid) {
00249     case CT_PASSENGERS:
00250       return avi->passenger_capacity;
00251     case CT_MAIL:
00252       return avi->passenger_capacity + avi->mail_capacity;
00253     case CT_GOODS:
00254       return (avi->passenger_capacity + avi->mail_capacity) / 2;
00255     default:
00256       return (avi->passenger_capacity + avi->mail_capacity) / 4;
00257   }
00258 }
00259 
00267 CommandCost CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00268 {
00269   if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);
00270 
00271   const AircraftVehicleInfo *avi = AircraftVehInfo(p1);
00272   CommandCost value = EstimateAircraftCost(p1, avi);
00273 
00274   /* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
00275   if (flags & DC_QUERY_COST) return value;
00276 
00277   if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00278 
00279   /* Prevent building aircraft types at places which can't handle them */
00280   if (!CanAircraftUseStation(p1, tile)) return CMD_ERROR;
00281 
00282   /* Allocate 2 or 3 vehicle structs, depending on type
00283    * vl[0] = aircraft, vl[1] = shadow, [vl[2] = rotor] */
00284   Vehicle *vl[3];
00285   if (!Vehicle::AllocateList(vl, avi->subtype & AIR_CTOL ? 2 : 3)) {
00286     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00287   }
00288 
00289   UnitID unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
00290   if (unit_num > _settings_game.vehicle.max_aircraft)
00291     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00292 
00293   if (flags & DC_EXEC) {
00294     Vehicle *v = vl[0]; // aircraft
00295     Vehicle *u = vl[1]; // shadow
00296 
00297     v = new (v) Aircraft();
00298     u = new (u) Aircraft();
00299     v->unitnumber = unit_num;
00300     v->direction = DIR_SE;
00301 
00302     v->owner = u->owner = _current_company;
00303 
00304     v->tile = tile;
00305 //    u->tile = 0;
00306 
00307     uint x = TileX(tile) * TILE_SIZE + 5;
00308     uint y = TileY(tile) * TILE_SIZE + 3;
00309 
00310     v->x_pos = u->x_pos = x;
00311     v->y_pos = u->y_pos = y;
00312 
00313     u->z_pos = GetSlopeZ(x, y);
00314     v->z_pos = u->z_pos + 1;
00315 
00316     v->running_ticks = 0;
00317 
00318 //    u->delta_x = u->delta_y = 0;
00319 
00320     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00321     u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00322 
00323     v->spritenum = avi->image_index;
00324 //    v->cargo_count = u->number_of_pieces = 0;
00325 
00326     v->cargo_cap = avi->passenger_capacity;
00327     u->cargo_cap = avi->mail_capacity;
00328 
00329     v->cargo_type = CT_PASSENGERS;
00330     u->cargo_type = CT_MAIL;
00331 
00332     v->cargo_subtype = 0;
00333 
00334     v->name = NULL;
00335 //    v->next_order_param = v->next_order = 0;
00336 
00337 //    v->load_unload_time_rem = 0;
00338 //    v->progress = 0;
00339     v->last_station_visited = INVALID_STATION;
00340 //    v->destination_coords = 0;
00341 
00342     v->max_speed = avi->max_speed;
00343     v->acceleration = avi->acceleration;
00344     v->engine_type = p1;
00345 
00346     v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00347     v->UpdateDeltaXY(INVALID_DIR);
00348     v->value = value.GetCost();
00349 
00350     u->subtype = AIR_SHADOW;
00351     u->UpdateDeltaXY(INVALID_DIR);
00352 
00353     /* Danger, Will Robinson!
00354      * If the aircraft is refittable, but cannot be refitted to
00355      * passengers, we select the cargo type from the refit mask.
00356      * This is a fairly nasty hack to get around the fact that TTD
00357      * has no default cargo type specifier for planes... */
00358     CargoID cargo = FindFirstRefittableCargo(p1);
00359     if (cargo != CT_INVALID && cargo != CT_PASSENGERS) {
00360       uint16 callback = CALLBACK_FAILED;
00361 
00362       v->cargo_type = cargo;
00363 
00364       if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00365         callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00366       }
00367 
00368       if (callback == CALLBACK_FAILED) {
00369         /* Callback failed, or not executed; use the default cargo capacity */
00370         v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
00371       } else {
00372         v->cargo_cap = callback;
00373       }
00374 
00375       /* Set the 'second compartent' capacity to none */
00376       u->cargo_cap = 0;
00377     }
00378 
00379     const Engine *e = GetEngine(p1);
00380     v->reliability = e->reliability;
00381     v->reliability_spd_dec = e->reliability_spd_dec;
00382     v->max_age = e->lifelength * 366;
00383 
00384     _new_vehicle_id = v->index;
00385 
00386     /* When we click on hangar we know the tile it is on. By that we know
00387      * its position in the array of depots the airport has.....we can search
00388      * layout for #th position of depot. Since layout must start with a listing
00389      * of all depots, it is simple */
00390     for (uint i = 0;; i++) {
00391       const Station *st = GetStationByTile(tile);
00392       const AirportFTAClass *apc = st->Airport();
00393 
00394       assert(i != apc->nof_depots);
00395       if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
00396         assert(apc->layout[i].heading == HANGAR);
00397         v->u.air.pos = apc->layout[i].position;
00398         break;
00399       }
00400     }
00401 
00402     v->u.air.state = HANGAR;
00403     v->u.air.previous_pos = v->u.air.pos;
00404     v->u.air.targetairport = GetStationIndex(tile);
00405     v->SetNext(u);
00406 
00407     v->service_interval = _settings_game.vehicle.servint_aircraft;
00408 
00409     v->date_of_last_service = _date;
00410     v->build_year = u->build_year = _cur_year;
00411 
00412     v->cur_image = u->cur_image = 0xEA0;
00413 
00414     v->random_bits = VehicleRandomBits();
00415     u->random_bits = VehicleRandomBits();
00416 
00417     v->vehicle_flags = 0;
00418     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00419 
00420     UpdateAircraftCache(v);
00421 
00422     VehiclePositionChanged(v);
00423     VehiclePositionChanged(u);
00424 
00425     /* Aircraft with 3 vehicles (chopper)? */
00426     if (v->subtype == AIR_HELICOPTER) {
00427       Vehicle *w = vl[2];
00428 
00429       w = new (w) Aircraft();
00430       w->direction = DIR_N;
00431       w->owner = _current_company;
00432       w->x_pos = v->x_pos;
00433       w->y_pos = v->y_pos;
00434       w->z_pos = v->z_pos + 5;
00435       w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00436       w->spritenum = 0xFF;
00437       w->subtype = AIR_ROTOR;
00438       w->cur_image = SPR_ROTOR_STOPPED;
00439       w->random_bits = VehicleRandomBits();
00440       /* Use rotor's air.state to store the rotor animation frame */
00441       w->u.air.state = HRS_ROTOR_STOPPED;
00442       w->UpdateDeltaXY(INVALID_DIR);
00443 
00444       u->SetNext(w);
00445       VehiclePositionChanged(w);
00446     }
00447 
00448     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00449     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00450     InvalidateWindow(WC_COMPANY, v->owner);
00451     if (IsLocalCompany())
00452       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); //updates the replace Aircraft window
00453 
00454     GetCompany(_current_company)->num_engines[p1]++;
00455   }
00456 
00457   return value;
00458 }
00459 
00460 
00461 static void DoDeleteAircraft(Vehicle *v)
00462 {
00463   DeleteWindowById(WC_VEHICLE_VIEW, v->index);
00464   InvalidateWindow(WC_COMPANY, v->owner);
00465   DeleteDepotHighlightOfVehicle(v);
00466   DeleteVehicleChain(v);
00467   InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00468 }
00469 
00477 CommandCost CmdSellAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00478 {
00479   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00480 
00481   Vehicle *v = GetVehicle(p1);
00482 
00483   if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00484   if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00485 
00486   if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
00487 
00488   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00489 
00490   if (flags & DC_EXEC) {
00491     // Invalidate depot
00492     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00493     DoDeleteAircraft(v);
00494   }
00495 
00496   return ret;
00497 }
00498 
00499 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00500 {
00501   const Station *st = GetTargetAirportIfValid(this);
00502   /* If the station is not a valid airport or if it has no hangars */
00503   if (st == NULL || st->Airport()->nof_depots == 0) {
00504     /* the aircraft has to search for a hangar on its own */
00505     StationID station = FindNearestHangar(this);
00506 
00507     if (station == INVALID_STATION) return false;
00508 
00509     st = GetStation(station);
00510   }
00511 
00512   if (location    != NULL) *location    = st->xy;
00513   if (destination != NULL) *destination = st->index;
00514 
00515   return true;
00516 }
00517 
00527 CommandCost CmdSendAircraftToHangar(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00528 {
00529   if (p2 & DEPOT_MASS_SEND) {
00530     /* Mass goto depot requested */
00531     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00532     return SendAllVehiclesToDepot(VEH_AIRCRAFT, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00533   }
00534 
00535   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00536 
00537   Vehicle *v = GetVehicle(p1);
00538 
00539   if (v->type != VEH_AIRCRAFT) return CMD_ERROR;
00540 
00541   return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00542 }
00543 
00544 
00555 CommandCost CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
00556 {
00557   byte new_subtype = GB(p2, 8, 8);
00558 
00559   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00560 
00561   Vehicle *v = GetVehicle(p1);
00562 
00563   if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00564   if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00565   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
00566 
00567   /* Check cargo */
00568   CargoID new_cid = GB(p2, 0, 8);
00569   if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
00570 
00571   /* Check the refit capacity callback */
00572   uint16 callback = CALLBACK_FAILED;
00573   if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00574     /* Back up the existing cargo type */
00575     CargoID temp_cid = v->cargo_type;
00576     byte temp_subtype = v->cargo_subtype;
00577     v->cargo_type = new_cid;
00578     v->cargo_subtype = new_subtype;
00579 
00580     callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00581 
00582     /* Restore the cargo type */
00583     v->cargo_type = temp_cid;
00584     v->cargo_subtype = temp_subtype;
00585   }
00586 
00587   const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00588 
00589   uint pass;
00590   if (callback == CALLBACK_FAILED) {
00591     /* If the callback failed, or wasn't executed, use the aircraft's
00592      * default cargo capacity */
00593     pass = AircraftDefaultCargoCapacity(new_cid, avi);
00594   } else {
00595     pass = callback;
00596   }
00597   _returned_refit_capacity = pass;
00598 
00599   CommandCost cost;
00600   if (IsHumanCompany(v->owner) && new_cid != v->cargo_type) {
00601     cost = GetRefitCost(v->engine_type);
00602   }
00603 
00604   if (flags & DC_EXEC) {
00605     v->cargo_cap = pass;
00606 
00607     Vehicle *u = v->Next();
00608     uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
00609     u->cargo_cap = mail;
00610     v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
00611     u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
00612     v->cargo_type = new_cid;
00613     v->cargo_subtype = new_subtype;
00614     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00615     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00616     InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00617   }
00618 
00619   return cost;
00620 }
00621 
00622 
00623 static void CheckIfAircraftNeedsService(Vehicle *v)
00624 {
00625   if (_settings_game.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00626   if (v->IsInDepot()) {
00627     VehicleServiceInDepot(v);
00628     return;
00629   }
00630 
00631   const Station *st = GetStation(v->current_order.GetDestination());
00632   /* only goto depot if the target airport has terminals (eg. it is airport) */
00633   if (st->IsValid() && st->airport_tile != 0 && st->Airport()->terminals != NULL) {
00634 //    printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
00635 //    v->u.air.targetairport = st->index;
00636     v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00637     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00638   } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00639     v->current_order.MakeDummy();
00640     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00641   }
00642 }
00643 
00644 void Aircraft::OnNewDay()
00645 {
00646   if (!IsNormalAircraft(this)) return;
00647 
00648   if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00649 
00650   CheckOrders(this);
00651 
00652   CheckVehicleBreakdown(this);
00653   AgeVehicle(this);
00654   CheckIfAircraftNeedsService(this);
00655 
00656   if (this->running_ticks == 0) return;
00657 
00658   CommandCost cost(EXPENSES_AIRCRAFT_RUN, GetVehicleProperty(this, 0x0E, AircraftVehInfo(this->engine_type)->running_cost) * _price.aircraft_running * this->running_ticks / (364 * DAY_TICKS));
00659 
00660   this->profit_this_year -= cost.GetCost();
00661   this->running_ticks = 0;
00662 
00663   SubtractMoneyFromCompanyFract(this->owner, cost);
00664 
00665   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
00666   InvalidateWindowClasses(WC_AIRCRAFT_LIST);
00667 }
00668 
00669 void AircraftYearlyLoop()
00670 {
00671   Vehicle *v;
00672 
00673   FOR_ALL_VEHICLES(v) {
00674     if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
00675       v->profit_last_year = v->profit_this_year;
00676       v->profit_this_year = 0;
00677       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00678     }
00679   }
00680 }
00681 
00682 static void AgeAircraftCargo(Vehicle *v)
00683 {
00684   if (_age_cargo_skip_counter != 0) return;
00685 
00686   do {
00687     v->cargo.AgeCargo();
00688     v = v->Next();
00689   } while (v != NULL);
00690 }
00691 
00692 static void HelicopterTickHandler(Vehicle *v)
00693 {
00694   Vehicle *u = v->Next()->Next();
00695 
00696   if (u->vehstatus & VS_HIDDEN) return;
00697 
00698   /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is
00699    * loading/unloading at a terminal or stopped */
00700   if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00701     if (u->cur_speed != 0) {
00702       u->cur_speed++;
00703       if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) {
00704         u->cur_speed = 0;
00705       }
00706     }
00707   } else {
00708     if (u->cur_speed == 0)
00709       u->cur_speed = 0x70;
00710 
00711     if (u->cur_speed >= 0x50)
00712       u->cur_speed--;
00713   }
00714 
00715   int tick = ++u->tick_counter;
00716   int spd = u->cur_speed >> 4;
00717 
00718   SpriteID img;
00719   if (spd == 0) {
00720     u->u.air.state = HRS_ROTOR_STOPPED;
00721     img = GetRotorImage(v);
00722     if (u->cur_image == img) return;
00723   } else if (tick >= spd) {
00724     u->tick_counter = 0;
00725     u->u.air.state++;
00726     if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1;
00727     img = GetRotorImage(v);
00728   } else {
00729     return;
00730   }
00731 
00732   u->cur_image = img;
00733 
00734   BeginVehicleMove(u);
00735   VehiclePositionChanged(u);
00736   EndVehicleMove(u);
00737 }
00738 
00739 static void SetAircraftPosition(Vehicle *v, int x, int y, int z)
00740 {
00741   v->x_pos = x;
00742   v->y_pos = y;
00743   v->z_pos = z;
00744 
00745   v->cur_image = v->GetImage(v->direction);
00746   if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00747 
00748   BeginVehicleMove(v);
00749   VehiclePositionChanged(v);
00750   EndVehicleMove(v);
00751 
00752   Vehicle *u = v->Next();
00753 
00754   int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00755   int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00756   u->x_pos = x;
00757   u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
00758 
00759   safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00760   u->z_pos = GetSlopeZ(safe_x, safe_y);
00761   u->cur_image = v->cur_image;
00762 
00763   BeginVehicleMove(u);
00764   VehiclePositionChanged(u);
00765   EndVehicleMove(u);
00766 
00767   u = u->Next();
00768   if (u != NULL) {
00769     u->x_pos = x;
00770     u->y_pos = y;
00771     u->z_pos = z + 5;
00772 
00773     BeginVehicleMove(u);
00774     VehiclePositionChanged(u);
00775     EndVehicleMove(u);
00776   }
00777 }
00778 
00782 void HandleAircraftEnterHangar(Vehicle *v)
00783 {
00784   v->subspeed = 0;
00785   v->progress = 0;
00786 
00787   Vehicle *u = v->Next();
00788   u->vehstatus |= VS_HIDDEN;
00789   u = u->Next();
00790   if (u != NULL) {
00791     u->vehstatus |= VS_HIDDEN;
00792     u->cur_speed = 0;
00793   }
00794 
00795   SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00796 }
00797 
00798 static void PlayAircraftSound(const Vehicle* v)
00799 {
00800   if (!PlayVehicleSound(v, VSE_START)) {
00801     SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00802   }
00803 }
00804 
00805 
00806 void UpdateAircraftCache(Vehicle *v)
00807 {
00808   uint max_speed = GetVehicleProperty(v, 0x0C, 0);
00809   if (max_speed != 0) {
00810     /* Convert from original units to (approx) km/h */
00811     max_speed = (max_speed * 129) / 10;
00812 
00813     v->u.air.cached_max_speed = max_speed;
00814   } else {
00815     v->u.air.cached_max_speed = 0xFFFF;
00816   }
00817 }
00818 
00819 
00823 enum AircraftSpeedLimits {
00824   SPEED_LIMIT_TAXI     =     50,  
00825   SPEED_LIMIT_APPROACH =    230,  
00826   SPEED_LIMIT_BROKEN   =    320,  
00827   SPEED_LIMIT_HOLD     =    425,  
00828   SPEED_LIMIT_NONE     = 0xFFFF   
00829 };
00830 
00838 static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00839 {
00840   uint spd = v->acceleration * 16;
00841   byte t;
00842 
00843   /* Adjust speed limits by plane speed factor to prevent taxiing
00844    * and take-off speeds being too low. */
00845   speed_limit *= _settings_game.vehicle.plane_speed;
00846 
00847   if (v->u.air.cached_max_speed < speed_limit) {
00848     if (v->cur_speed < speed_limit) hard_limit = false;
00849     speed_limit = v->u.air.cached_max_speed;
00850   }
00851 
00852   speed_limit = min(speed_limit, v->max_speed);
00853 
00854   v->subspeed = (t=v->subspeed) + (byte)spd;
00855 
00856   /* Aircraft's current speed is used twice so that very fast planes are
00857    * forced to slow down rapidly in the short distance needed. The magic
00858    * value 16384 was determined to give similar results to the old speed/48
00859    * method at slower speeds. This also results in less reduction at slow
00860    * speeds to that aircraft do not get to taxi speed straight after
00861    * touchdown. */
00862   if (!hard_limit && v->cur_speed > speed_limit) {
00863     speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00864   }
00865 
00866   spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00867 
00868   /* adjust speed for broken vehicles */
00869   if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00870 
00871   /* updates statusbar only if speed have changed to save CPU time */
00872   if (spd != v->cur_speed) {
00873     v->cur_speed = spd;
00874     if (_settings_client.gui.vehicle_speed)
00875       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00876   }
00877 
00878   /* Adjust distance moved by plane speed setting */
00879   if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00880 
00881   if (!(v->direction & 1)) spd = spd * 3 / 4;
00882 
00883   spd += v->progress;
00884   v->progress = (byte)spd;
00885   return spd >> 8;
00886 }
00887 
00895 static byte GetAircraftFlyingAltitude(const Vehicle *v)
00896 {
00897   /* Make sure Aircraft fly no lower so that they don't conduct
00898    * CFITs (controlled flight into terrain)
00899    */
00900   byte base_altitude = 150;
00901 
00902   /* Make sure eastbound and westbound planes do not "crash" into each
00903    * other by providing them with vertical seperation
00904    */
00905   switch (v->direction) {
00906     case DIR_N:
00907     case DIR_NE:
00908     case DIR_E:
00909     case DIR_SE:
00910       base_altitude += 10;
00911       break;
00912 
00913     default: break;
00914   }
00915 
00916   /* Make faster planes fly higher so that they can overtake slower ones */
00917   base_altitude += min(20 * (v->max_speed / 200), 90);
00918 
00919   return base_altitude;
00920 }
00921 
00935 static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
00936 {
00937   assert(v != NULL);
00938   assert(apc != NULL);
00939 
00940   /* In the case the station doesn't exit anymore, set target tile 0.
00941    * It doesn't hurt much, aircraft will go to next order, nearest hangar
00942    * or it will simply crash in next tick */
00943   TileIndex tile = 0;
00944 
00945   if (IsValidStationID(v->u.air.targetairport)) {
00946     const Station *st = GetStation(v->u.air.targetairport);
00947     /* Make sure we don't go to 0,0 if the airport has been removed. */
00948     tile = (st->airport_tile != 0) ? st->airport_tile : st->xy;
00949   }
00950 
00951   int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00952   int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00953 
00954   DiagDirection dir;
00955   if (abs(delta_y) < abs(delta_x)) {
00956     /* We are northeast or southwest of the airport */
00957     dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00958   } else {
00959     /* We are northwest or southeast of the airport */
00960     dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00961   }
00962   return apc->entry_points[dir];
00963 }
00964 
00972 static bool AircraftController(Vehicle *v)
00973 {
00974   int count;
00975 
00976   /* NULL if station is invalid */
00977   const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00978   /* 0 if there is no station */
00979   TileIndex tile = 0;
00980   if (st != NULL) {
00981     tile = st->airport_tile;
00982     if (tile == 0) tile = st->xy;
00983   }
00984   /* DUMMY if there is no station or no airport */
00985   const AirportFTAClass *afc = tile == 0 ? GetAirport(AT_DUMMY) : st->Airport();
00986 
00987   /* prevent going to 0,0 if airport is deleted. */
00988   if (st == NULL || st->airport_tile == 0) {
00989     /* Jump into our "holding pattern" state machine if possible */
00990     if (v->u.air.pos >= afc->nofelements) {
00991       v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
00992     } else if (v->u.air.targetairport != v->current_order.GetDestination()) {
00993       /* If not possible, just get out of here fast */
00994       v->u.air.state = FLYING;
00995       UpdateAircraftCache(v);
00996       AircraftNextAirportPos_and_Order(v);
00997       /* get aircraft back on running altitude */
00998       SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00999       return false;
01000     }
01001   }
01002 
01003   /*  get airport moving data */
01004   const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
01005 
01006   int x = TileX(tile) * TILE_SIZE;
01007   int y = TileY(tile) * TILE_SIZE;
01008 
01009   /* Helicopter raise */
01010   if (amd->flag & AMED_HELI_RAISE) {
01011     Vehicle *u = v->Next()->Next();
01012 
01013     /* Make sure the rotors don't rotate too fast */
01014     if (u->cur_speed > 32) {
01015       v->cur_speed = 0;
01016       if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
01017     } else {
01018       u->cur_speed = 32;
01019       count = UpdateAircraftSpeed(v);
01020       if (count > 0) {
01021         v->tile = 0;
01022 
01023         /* Reached altitude? */
01024         if (v->z_pos >= 184) {
01025           v->cur_speed = 0;
01026           return true;
01027         }
01028         SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
01029       }
01030     }
01031     return false;
01032   }
01033 
01034   /* Helicopter landing. */
01035   if (amd->flag & AMED_HELI_LOWER) {
01036     if (st == NULL) {
01037       /* FIXME - AircraftController -> if station no longer exists, do not land
01038        * helicopter will circle until sign disappears, then go to next order
01039        * what to do when it is the only order left, right now it just stays in 1 place */
01040       v->u.air.state = FLYING;
01041       UpdateAircraftCache(v);
01042       AircraftNextAirportPos_and_Order(v);
01043       return false;
01044     }
01045 
01046     /* Vehicle is now at the airport. */
01047     v->tile = tile;
01048 
01049     /* Find altitude of landing position. */
01050     int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
01051 
01052     if (z == v->z_pos) {
01053       Vehicle *u = v->Next()->Next();
01054 
01055       /*  Increase speed of rotors. When speed is 80, we've landed. */
01056       if (u->cur_speed >= 80) return true;
01057       u->cur_speed += 4;
01058     } else {
01059       count = UpdateAircraftSpeed(v);
01060       if (count > 0) {
01061         if (v->z_pos > z) {
01062           SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
01063         } else {
01064           SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
01065         }
01066       }
01067     }
01068     return false;
01069   }
01070 
01071   /* Get distance from destination pos to current pos. */
01072   uint dist = abs(x + amd->x - v->x_pos) +  abs(y + amd->y - v->y_pos);
01073 
01074   /* Need exact position? */
01075   if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01076 
01077   /* At final pos? */
01078   if (dist == 0) {
01079     /* Change direction smoothly to final direction. */
01080     DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01081     /* if distance is 0, and plane points in right direction, no point in calling
01082      * UpdateAircraftSpeed(). So do it only afterwards */
01083     if (dirdiff == DIRDIFF_SAME) {
01084       v->cur_speed = 0;
01085       return true;
01086     }
01087 
01088     if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
01089 
01090     v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01091     v->cur_speed >>= 1;
01092 
01093     SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01094     return false;
01095   }
01096 
01097   uint speed_limit = SPEED_LIMIT_TAXI;
01098   bool hard_limit = true;
01099 
01100   if (amd->flag & AMED_NOSPDCLAMP)   speed_limit = SPEED_LIMIT_NONE;
01101   if (amd->flag & AMED_HOLD)       { speed_limit = SPEED_LIMIT_HOLD;     hard_limit = false; }
01102   if (amd->flag & AMED_LAND)       { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
01103   if (amd->flag & AMED_BRAKE)      { speed_limit = SPEED_LIMIT_TAXI;     hard_limit = false; }
01104 
01105   count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
01106   if (count == 0) return false;
01107 
01108   if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
01109 
01110   do {
01111 
01112     GetNewVehiclePosResult gp;
01113 
01114     if (dist < 4 || amd->flag & AMED_LAND) {
01115       /* move vehicle one pixel towards target */
01116       gp.x = (v->x_pos != (x + amd->x)) ?
01117           v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
01118           v->x_pos;
01119       gp.y = (v->y_pos != (y + amd->y)) ?
01120           v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01121           v->y_pos;
01122 
01123       /* Oilrigs must keep v->tile as st->airport_tile, since the landing pad is in a non-airport tile */
01124       gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01125 
01126     } else {
01127 
01128       /* Turn. Do it slowly if in the air. */
01129       Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01130       if (newdir != v->direction) {
01131         v->direction = newdir;
01132         if (amd->flag & AMED_SLOWTURN) {
01133           if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
01134         } else {
01135           v->cur_speed >>= 1;
01136         }
01137       }
01138 
01139       /* Move vehicle. */
01140       gp = GetNewVehiclePos(v);
01141     }
01142 
01143     v->tile = gp.new_tile;
01144     /* If vehicle is in the air, use tile coordinate 0. */
01145     if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01146 
01147     /* Adjust Z for land or takeoff? */
01148     uint z = v->z_pos;
01149 
01150     if (amd->flag & AMED_TAKEOFF) {
01151       z = min(z + 2, GetAircraftFlyingAltitude(v));
01152     }
01153 
01154     if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01155 
01156     if (amd->flag & AMED_LAND) {
01157       if (st->airport_tile == 0) {
01158         /* Airport has been removed, abort the landing procedure */
01159         v->u.air.state = FLYING;
01160         UpdateAircraftCache(v);
01161         AircraftNextAirportPos_and_Order(v);
01162         /* get aircraft back on running altitude */