00001
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
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
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
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
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
00247
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
00275 if (flags & DC_QUERY_COST) return value;
00276
00277 if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00278
00279
00280 if (!CanAircraftUseStation(p1, tile)) return CMD_ERROR;
00281
00282
00283
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];
00295 Vehicle *u = vl[1];
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
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
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
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
00336
00337
00338
00339 v->last_station_visited = INVALID_STATION;
00340
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
00354
00355
00356
00357
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
00370 v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
00371 } else {
00372 v->cargo_cap = callback;
00373 }
00374
00375
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
00387
00388
00389
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
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
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);
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
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
00503 if (st == NULL || st->Airport()->nof_depots == 0) {
00504
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
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
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
00572 uint16 callback = CALLBACK_FAILED;
00573 if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00574
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
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
00592
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
00633 if (st->IsValid() && st->airport_tile != 0 && st->Airport()->terminals != NULL) {
00634
00635
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
00699
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
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
00844
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
00857
00858
00859
00860
00861
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
00869 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00870
00871
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
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
00898
00899
00900 byte base_altitude = 150;
00901
00902
00903
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
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
00941
00942
00943 TileIndex tile = 0;
00944
00945 if (IsValidStationID(v->u.air.targetairport)) {
00946 const Station *st = GetStation(v->u.air.targetairport);
00947
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
00957 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00958 } else {
00959
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
00977 const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00978
00979 TileIndex tile = 0;
00980 if (st != NULL) {
00981 tile = st->airport_tile;
00982 if (tile == 0) tile = st->xy;
00983 }
00984
00985 const AirportFTAClass *afc = tile == 0 ? GetAirport(AT_DUMMY) : st->Airport();
00986
00987
00988 if (st == NULL || st->airport_tile == 0) {
00989
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
00994 v->u.air.state = FLYING;
00995 UpdateAircraftCache(v);
00996 AircraftNextAirportPos_and_Order(v);
00997
00998 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00999 return false;
01000 }
01001 }
01002
01003
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
01010 if (amd->flag & AMED_HELI_RAISE) {
01011 Vehicle *u = v->Next()->Next();
01012
01013
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
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
01035 if (amd->flag & AMED_HELI_LOWER) {
01036 if (st == NULL) {
01037
01038
01039
01040 v->u.air.state = FLYING;
01041 UpdateAircraftCache(v);
01042 AircraftNextAirportPos_and_Order(v);
01043 return false;
01044 }
01045
01046
01047 v->tile = tile;
01048
01049
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
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
01072 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
01073
01074
01075 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01076
01077
01078 if (dist == 0) {
01079
01080 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01081
01082
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
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
01124 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01125
01126 } else {
01127
01128
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
01140 gp = GetNewVehiclePos(v);
01141 }
01142
01143 v->tile = gp.new_tile;
01144
01145 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01146
01147
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
01159 v->u.air.state = FLYING;
01160 UpdateAircraftCache(v);
01161 AircraftNextAirportPos_and_Order(v);
01162