00001
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
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
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
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
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
00229
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
00258 if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00259
00260
00261 if (flags & DC_QUERY_COST) return value;
00262
00263 if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00264
00265
00266 if (!CanVehicleUseStation(p1, GetStationByTile(tile))) return CMD_ERROR;
00267
00268
00269
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];
00281 Vehicle *u = vl[1];
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
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
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
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
00322
00323
00324
00325 v->last_station_visited = INVALID_STATION;
00326
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 v->reliability = e->reliability;
00341 v->reliability_spd_dec = e->reliability_spd_dec;
00342 v->max_age = e->lifelength * DAYS_IN_LEAP_YEAR;
00343
00344 _new_vehicle_id = v->index;
00345
00346
00347
00348
00349
00350 for (uint i = 0;; i++) {
00351 const Station *st = GetStationByTile(tile);
00352 const AirportFTAClass *apc = st->Airport();
00353
00354 assert(i != apc->nof_depots);
00355 if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
00356 assert(apc->layout[i].heading == HANGAR);
00357 v->u.air.pos = apc->layout[i].position;
00358 break;
00359 }
00360 }
00361
00362 v->u.air.state = HANGAR;
00363 v->u.air.previous_pos = v->u.air.pos;
00364 v->u.air.targetairport = GetStationIndex(tile);
00365 v->SetNext(u);
00366
00367 v->service_interval = _settings_game.vehicle.servint_aircraft;
00368
00369 v->date_of_last_service = _date;
00370 v->build_year = u->build_year = _cur_year;
00371
00372 v->cur_image = u->cur_image = 0xEA0;
00373
00374 v->random_bits = VehicleRandomBits();
00375 u->random_bits = VehicleRandomBits();
00376
00377 v->vehicle_flags = 0;
00378 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00379
00380 v->InvalidateNewGRFCacheOfChain();
00381
00382 if (v->cargo_type != CT_PASSENGERS) {
00383 uint16 callback = CALLBACK_FAILED;
00384
00385 if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00386 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00387 }
00388
00389 if (callback == CALLBACK_FAILED) {
00390
00391 v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
00392 } else {
00393 v->cargo_cap = callback;
00394 }
00395
00396
00397 u->cargo_cap = 0;
00398 }
00399
00400 v->InvalidateNewGRFCacheOfChain();
00401
00402 UpdateAircraftCache(v);
00403
00404 VehicleMove(v, false);
00405 VehicleMove(u, false);
00406
00407
00408 if (v->subtype == AIR_HELICOPTER) {
00409 Vehicle *w = vl[2];
00410
00411 w = new (w) Aircraft();
00412 w->engine_type = p1;
00413 w->direction = DIR_N;
00414 w->owner = _current_company;
00415 w->x_pos = v->x_pos;
00416 w->y_pos = v->y_pos;
00417 w->z_pos = v->z_pos + 5;
00418 w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00419 w->spritenum = 0xFF;
00420 w->subtype = AIR_ROTOR;
00421 w->cur_image = SPR_ROTOR_STOPPED;
00422 w->random_bits = VehicleRandomBits();
00423
00424 w->u.air.state = HRS_ROTOR_STOPPED;
00425 w->UpdateDeltaXY(INVALID_DIR);
00426
00427 u->SetNext(w);
00428 VehicleMove(w, false);
00429 }
00430
00431 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00432 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00433 InvalidateWindow(WC_COMPANY, v->owner);
00434 if (IsLocalCompany())
00435 InvalidateAutoreplaceWindow(v->engine_type, v->group_id);
00436
00437 GetCompany(_current_company)->num_engines[p1]++;
00438 }
00439
00440 return value;
00441 }
00442
00443
00451 CommandCost CmdSellAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00452 {
00453 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00454
00455 Vehicle *v = GetVehicle(p1);
00456
00457 if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00458 if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00459
00460 if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
00461
00462 CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00463
00464 if (flags & DC_EXEC) {
00465 delete v;
00466 }
00467
00468 return ret;
00469 }
00470
00471 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00472 {
00473 const Station *st = GetTargetAirportIfValid(this);
00474
00475 if (st == NULL || st->Airport()->nof_depots == 0) {
00476
00477 StationID station = FindNearestHangar(this);
00478
00479 if (station == INVALID_STATION) return false;
00480
00481 st = GetStation(station);
00482 }
00483
00484 if (location != NULL) *location = st->xy;
00485 if (destination != NULL) *destination = st->index;
00486
00487 return true;
00488 }
00489
00499 CommandCost CmdSendAircraftToHangar(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00500 {
00501 if (p2 & DEPOT_MASS_SEND) {
00502
00503 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00504 return SendAllVehiclesToDepot(VEH_AIRCRAFT, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00505 }
00506
00507 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00508
00509 Vehicle *v = GetVehicle(p1);
00510
00511 if (v->type != VEH_AIRCRAFT) return CMD_ERROR;
00512
00513 return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00514 }
00515
00516
00527 CommandCost CmdRefitAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00528 {
00529 byte new_subtype = GB(p2, 8, 8);
00530
00531 if (!IsValidVehicleID(p1)) return CMD_ERROR;
00532
00533 Vehicle *v = GetVehicle(p1);
00534
00535 if (v->type != VEH_AIRCRAFT || !CheckOwnership(v->owner)) return CMD_ERROR;
00536 if (!v->IsStoppedInDepot()) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED);
00537 if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
00538
00539
00540 CargoID new_cid = GB(p2, 0, 8);
00541 if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
00542
00543
00544 uint16 callback = CALLBACK_FAILED;
00545 if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00546
00547 CargoID temp_cid = v->cargo_type;
00548 byte temp_subtype = v->cargo_subtype;
00549 v->cargo_type = new_cid;
00550 v->cargo_subtype = new_subtype;
00551
00552 callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00553
00554
00555 v->cargo_type = temp_cid;
00556 v->cargo_subtype = temp_subtype;
00557 }
00558
00559 const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00560
00561 uint pass;
00562 if (callback == CALLBACK_FAILED) {
00563
00564
00565 pass = AircraftDefaultCargoCapacity(new_cid, avi);
00566 } else {
00567 pass = callback;
00568 }
00569 _returned_refit_capacity = pass;
00570
00571 CommandCost cost;
00572 if (new_cid != v->cargo_type) {
00573 cost = GetRefitCost(v->engine_type);
00574 }
00575
00576 if (flags & DC_EXEC) {
00577 v->cargo_cap = pass;
00578
00579 Vehicle *u = v->Next();
00580 uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
00581 u->cargo_cap = mail;
00582 v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
00583 u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
00584 v->cargo_type = new_cid;
00585 v->cargo_subtype = new_subtype;
00586 v->colourmap = PAL_NONE;
00587 v->InvalidateNewGRFCacheOfChain();
00588 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00589 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00590 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00591 }
00592
00593 return cost;
00594 }
00595
00596
00597 static void CheckIfAircraftNeedsService(Vehicle *v)
00598 {
00599 if (_settings_game.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00600 if (v->IsInDepot()) {
00601 VehicleServiceInDepot(v);
00602 return;
00603 }
00604
00605 const Station *st = GetStation(v->current_order.GetDestination());
00606
00607 if (st->IsValid() && st->airport_tile != INVALID_TILE && st->Airport()->terminals != NULL) {
00608
00609
00610 v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00611 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00612 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00613 v->current_order.MakeDummy();
00614 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00615 }
00616 }
00617
00618 Money Aircraft::GetRunningCost() const
00619 {
00620 return GetVehicleProperty(this, 0x0E, AircraftVehInfo(this->engine_type)->running_cost) * _price.aircraft_running;
00621 }
00622
00623 void Aircraft::OnNewDay()
00624 {
00625 if (!IsNormalAircraft(this)) return;
00626
00627 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00628
00629 CheckOrders(this);
00630
00631 CheckVehicleBreakdown(this);
00632 AgeVehicle(this);
00633 CheckIfAircraftNeedsService(this);
00634
00635 if (this->running_ticks == 0) return;
00636
00637 CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00638
00639 this->profit_this_year -= cost.GetCost();
00640 this->running_ticks = 0;
00641
00642 SubtractMoneyFromCompanyFract(this->owner, cost);
00643
00644 InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
00645 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
00646 }
00647
00648 static void AgeAircraftCargo(Vehicle *v)
00649 {
00650 if (_age_cargo_skip_counter != 0) return;
00651
00652 do {
00653 v->cargo.AgeCargo();
00654 v = v->Next();
00655 } while (v != NULL);
00656 }
00657
00658 static void HelicopterTickHandler(Vehicle *v)
00659 {
00660 Vehicle *u = v->Next()->Next();
00661
00662 if (u->vehstatus & VS_HIDDEN) return;
00663
00664
00665
00666 if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00667 if (u->cur_speed != 0) {
00668 u->cur_speed++;
00669 if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) {
00670 u->cur_speed = 0;
00671 }
00672 }
00673 } else {
00674 if (u->cur_speed == 0)
00675 u->cur_speed = 0x70;
00676
00677 if (u->cur_speed >= 0x50)
00678 u->cur_speed--;
00679 }
00680
00681 int tick = ++u->tick_counter;
00682 int spd = u->cur_speed >> 4;
00683
00684 SpriteID img;
00685 if (spd == 0) {
00686 u->u.air.state = HRS_ROTOR_STOPPED;
00687 img = GetRotorImage(v);
00688 if (u->cur_image == img) return;
00689 } else if (tick >= spd) {
00690 u->tick_counter = 0;
00691 u->u.air.state++;
00692 if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1;
00693 img = GetRotorImage(v);
00694 } else {
00695 return;
00696 }
00697
00698 u->cur_image = img;
00699
00700 VehicleMove(u, true);
00701 }
00702
00703 void SetAircraftPosition(Vehicle *v, int x, int y, int z)
00704 {
00705 v->x_pos = x;
00706 v->y_pos = y;
00707 v->z_pos = z;
00708
00709 v->cur_image = v->GetImage(v->direction);
00710 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00711
00712 VehicleMove(v, true);
00713
00714 Vehicle *u = v->Next();
00715
00716 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00717 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00718 u->x_pos = x;
00719 u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
00720
00721 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00722 u->z_pos = GetSlopeZ(safe_x, safe_y);
00723 u->cur_image = v->cur_image;
00724
00725 VehicleMove(u, true);
00726
00727 u = u->Next();
00728 if (u != NULL) {
00729 u->x_pos = x;
00730 u->y_pos = y;
00731 u->z_pos = z + 5;
00732
00733 VehicleMove(u, true);
00734 }
00735 }
00736
00740 void HandleAircraftEnterHangar(Vehicle *v)
00741 {
00742 v->subspeed = 0;
00743 v->progress = 0;
00744
00745 Vehicle *u = v->Next();
00746 u->vehstatus |= VS_HIDDEN;
00747 u = u->Next();
00748 if (u != NULL) {
00749 u->vehstatus |= VS_HIDDEN;
00750 u->cur_speed = 0;
00751 }
00752
00753 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00754 }
00755
00756 static void PlayAircraftSound(const Vehicle *v)
00757 {
00758 if (!PlayVehicleSound(v, VSE_START)) {
00759 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00760 }
00761 }
00762
00763
00764 void UpdateAircraftCache(Vehicle *v)
00765 {
00766 uint max_speed = GetVehicleProperty(v, 0x0C, 0);
00767 if (max_speed != 0) {
00768
00769 max_speed = (max_speed * 129) / 10;
00770
00771 v->u.air.cached_max_speed = max_speed;
00772 } else {
00773 v->u.air.cached_max_speed = 0xFFFF;
00774 }
00775 }
00776
00777
00781 enum AircraftSpeedLimits {
00782 SPEED_LIMIT_TAXI = 50,
00783 SPEED_LIMIT_APPROACH = 230,
00784 SPEED_LIMIT_BROKEN = 320,
00785 SPEED_LIMIT_HOLD = 425,
00786 SPEED_LIMIT_NONE = 0xFFFF
00787 };
00788
00796 static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00797 {
00798 uint spd = v->acceleration * 16;
00799 byte t;
00800
00801
00802
00803 speed_limit *= _settings_game.vehicle.plane_speed;
00804
00805 if (v->u.air.cached_max_speed < speed_limit) {
00806 if (v->cur_speed < speed_limit) hard_limit = false;
00807 speed_limit = v->u.air.cached_max_speed;
00808 }
00809
00810 speed_limit = min(speed_limit, v->max_speed);
00811
00812 v->subspeed = (t=v->subspeed) + (byte)spd;
00813
00814
00815
00816
00817
00818
00819
00820 if (!hard_limit && v->cur_speed > speed_limit) {
00821 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00822 }
00823
00824 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00825
00826
00827 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00828
00829
00830 if (spd != v->cur_speed) {
00831 v->cur_speed = spd;
00832 if (_settings_client.gui.vehicle_speed)
00833 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00834 }
00835
00836
00837 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00838
00839 if (!(v->direction & 1)) spd = spd * 3 / 4;
00840
00841 spd += v->progress;
00842 v->progress = (byte)spd;
00843 return spd >> 8;
00844 }
00845
00853 byte GetAircraftFlyingAltitude(const Vehicle *v)
00854 {
00855
00856
00857
00858 byte base_altitude = 150;
00859
00860
00861
00862
00863 switch (v->direction) {
00864 case DIR_N:
00865 case DIR_NE:
00866 case DIR_E:
00867 case DIR_SE:
00868 base_altitude += 10;
00869 break;
00870
00871 default: break;
00872 }
00873
00874
00875 base_altitude += min(20 * (v->max_speed / 200), 90);
00876
00877 return base_altitude;
00878 }
00879
00893 static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
00894 {
00895 assert(v != NULL);
00896 assert(apc != NULL);
00897
00898
00899
00900
00901 TileIndex tile = 0;
00902
00903 if (IsValidStationID(v->u.air.targetairport)) {
00904 const Station *st = GetStation(v->u.air.targetairport);
00905
00906 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00907 }
00908
00909 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00910 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00911
00912 DiagDirection dir;
00913 if (abs(delta_y) < abs(delta_x)) {
00914
00915 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00916 } else {
00917
00918 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00919 }
00920 return apc->entry_points[dir];
00921 }
00922
00930 static bool AircraftController(Vehicle *v)
00931 {
00932 int count;
00933
00934
00935 const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00936
00937 TileIndex tile = INVALID_TILE;
00938 if (st != NULL) {
00939 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00940 }
00941
00942 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00943
00944
00945 if (st == NULL || st->airport_tile == INVALID_TILE) {
00946
00947 if (v->u.air.pos >= afc->nofelements) {
00948 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
00949 } else if (v->u.air.targetairport != v->current_order.GetDestination()) {
00950
00951 v->u.air.state = FLYING;
00952 UpdateAircraftCache(v);
00953 AircraftNextAirportPos_and_Order(v);
00954
00955 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00956 return false;
00957 }
00958 }
00959
00960
00961 const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
00962
00963 int x = TileX(tile) * TILE_SIZE;
00964 int y = TileY(tile) * TILE_SIZE;
00965
00966
00967 if (amd->flag & AMED_HELI_RAISE) {
00968 Vehicle *u = v->Next()->Next();
00969
00970
00971 if (u->cur_speed > 32) {
00972 v->cur_speed = 0;
00973 if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00974 } else {
00975 u->cur_speed = 32;
00976 count = UpdateAircraftSpeed(v);
00977 if (count > 0) {
00978 v->tile = 0;
00979
00980
00981 if (v->z_pos >= 184) {
00982 v->cur_speed = 0;
00983 return true;
00984 }
00985 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00986 }
00987 }
00988 return false;
00989 }
00990
00991
00992 if (amd->flag & AMED_HELI_LOWER) {
00993 if (st == NULL) {
00994
00995
00996
00997 v->u.air.state = FLYING;
00998 UpdateAircraftCache(v);
00999 AircraftNextAirportPos_and_Order(v);
01000 return false;
01001 }
01002
01003
01004 v->tile = tile;
01005
01006
01007 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
01008
01009 if (z == v->z_pos) {
01010 Vehicle *u = v->Next()->Next();
01011
01012
01013 if (u->cur_speed >= 80) return true;
01014 u->cur_speed += 4;
01015 } else {
01016 count = UpdateAircraftSpeed(v);
01017 if (count > 0) {
01018 if (v->z_pos > z) {
01019 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
01020 } else {
01021 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
01022 }
01023 }
01024 }
01025 return false;
01026 }
01027
01028
01029 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
01030
01031
01032 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01033
01034
01035 if (dist == 0) {
01036
01037 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01038
01039
01040 if (dirdiff == DIRDIFF_SAME) {
01041 v->cur_speed = 0;
01042 return true;
01043 }
01044
01045 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
01046
01047 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01048 v->cur_speed >>= 1;
01049
01050 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01051 return false;
01052 }
01053
01054 uint speed_limit = SPEED_LIMIT_TAXI;
01055 bool hard_limit = true;
01056
01057 if (amd->flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
01058 if (amd->flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
01059 if (amd->flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
01060 if (amd->flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
01061
01062 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
01063 if (count == 0) return false;
01064
01065 if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
01066
01067 do {
01068
01069 GetNewVehiclePosResult gp;
01070
01071 if (dist < 4 || amd->flag & AMED_LAND) {
01072
01073 gp.x = (v->x_pos != (x + amd->x)) ?
01074 v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
01075 v->x_pos;
01076 gp.y = (v->y_pos != (y + amd->y)) ?
01077 v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01078 v->y_pos;
01079
01080
01081 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01082
01083 } else {
01084
01085
01086 Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01087 if (newdir != v->direction) {
01088 v->direction = newdir;
01089 if (amd->flag & AMED_SLOWTURN) {
01090 if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
01091 } else {
01092 v->cur_speed >>= 1;
01093 }
01094 }
01095
01096
01097 gp = GetNewVehiclePos(v);
01098 }
01099
01100 v->tile = gp.new_tile;
01101
01102 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01103
01104
01105 uint z = v->z_pos;
01106
01107 if (amd->flag & AMED_TAKEOFF) {
01108 z = min(z + 2, GetAircraftFlyingAltitude(v));
01109 }
01110
01111 if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01112
01113 if (amd->flag & AMED_LAND) {
01114 if (st->airport_tile == INVALID_TILE) {
01115
01116 v->u.air.state = FLYING;
01117 UpdateAircraftCache(v);
01118 AircraftNextAirportPos_and_Order(v);
01119
01120 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01121 continue;
01122 }
01123
01124 uint curz = GetSlopeZ(x, y) + 1;
01125
01126 if (curz > z) {
01127 z++;
01128 } else {
01129 int t = max(1U, dist - 4);
01130
01131 z -= ((z - curz) + t - 1) / t;
01132 if (z < curz) z = curz;
01133 }
01134 }
01135
01136
01137 if (amd->flag & AMED_BRAKE) {
01138 uint curz = GetSlopeZ(x, y) + 1;
01139
01140 if (z > curz) {
01141 z--;
01142 } else if (z < curz) {
01143 z++;
01144 }
01145
01146 }
01147
01148 SetAircraftPosition(v, gp.x, gp.y, z);
01149 } while (--count != 0);
01150 return false;
01151 }
01152
01153
01154 static void HandleCrashedAircraft(Vehicle *v)
01155 {
01156 v->u.air.crashed_counter += 3;
01157
01158 Station *st = GetTargetAirportIfValid(v);
01159
01160
01161 if (v->u.air.crashed_counter < 500 && st == NULL && ((v->u.air.crashed_counter % 3) == 0) ) {
01162 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01163 v->z_pos -= 1;
01164 if (v->z_pos == z) {
01165 v->u.air.crashed_counter = 500;
01166 v->z_pos++;
01167 }
01168 }
01169
01170 if (v->u.air.crashed_counter < 650) {
01171 uint32 r;
01172 if (Chance16R(1,32,r)) {
01173 static const DirDiff delta[] = {
01174 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01175 };
01176
01177 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01178 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01179 r = Random();
01180 CreateEffectVehicleRel(v,
01181 GB(r, 0, 4) - 4,
01182 GB(r, 4, 4) - 4,
01183 GB(r, 8, 4),
01184 EV_EXPLOSION_SMALL);
01185 }
01186 } else if (v->u.air.crashed_counter >= 10000) {
01187
01188
01189
01190
01191
01192 if (st != NULL) {
01193 CLRBITS(st->airport_flags, RUNWAY_IN_block);
01194 CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block);
01195 CLRBITS(st->airport_flags, RUNWAY_IN2_block);
01196 }
01197
01198 delete v;
01199 }
01200 }
01201
01202 static void HandleBrokenAircraft(Vehicle *v)
01203 {
01204 if (v->breakdown_ctr != 1) {
01205 v->breakdown_ctr = 1;
01206 v->vehstatus |= VS_AIRCRAFT_BROKEN;
01207
01208 if (v->breakdowns_since_last_service != 255)
01209 v->breakdowns_since_last_service++;
01210 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01211 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01212 }
01213 }
01214
01215
01216 static void HandleAircraftSmoke(Vehicle *v)
01217 {
01218 static const struct {
01219 int8 x;
01220 int8 y;
01221 } smoke_pos[] = {
01222 { 5, 5 },
01223 { 6, 0 },
01224 { 5, -5 },
01225 { 0, -6 },
01226 { -5, -5 },
01227 { -6, 0 },
01228 { -5, 5 },
01229 { 0, 6 }
01230 };
01231
01232 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01233
01234 if (v->cur_speed < 10) {
01235 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01236 v->breakdown_ctr = 0;
01237 return;
01238 }
01239
01240 if ((v->tick_counter & 0x1F) == 0) {
01241 CreateEffectVehicleRel(v,
01242 smoke_pos[v->direction].x,
01243 smoke_pos[v->direction].y,
01244 2,
01245 EV_SMOKE
01246 );
01247 }
01248 }
01249
01250 void HandleMissingAircraftOrders(Vehicle *v)
01251 {
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267 const Station *st = GetTargetAirportIfValid(v);
01268 if (st == NULL) {
01269 CommandCost ret;
01270 CompanyID old_company = _current_company;
01271
01272 _current_company = v->owner;
01273 ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01274 _current_company = old_company;
01275
01276 if (CmdFailed(ret)) CrashAirplane(v);
01277 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01278 v->current_order.Free();
01279 }
01280 }
01281
01282
01283 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01284 {
01285
01286 if (this->u.air.state == FLYING) {
01287 AircraftNextAirportPos_and_Order(this);
01288 }
01289
01290
01291 return 0;
01292 }
01293
01294 void Aircraft::MarkDirty()
01295 {
01296 this->cur_image = this->GetImage(this->direction);
01297 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01298 MarkSingleVehicleDirty(this);
01299 }
01300
01301 static void CrashAirplane(Vehicle *v)
01302 {
01303 v->vehstatus |= VS_CRASHED;
01304 v->u.air.crashed_counter = 0;
01305
01306 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01307
01308 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01309
01310 uint amt = 2;
01311 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count();
01312 SetDParam(0, amt);
01313
01314 v->cargo.Truncate(0);
01315 v->Next()->cargo.Truncate(0);
01316 const Station *st = GetTargetAirportIfValid(v);
01317 StringID newsitem;
01318 AIEventVehicleCrashed::CrashReason crash_reason;
01319 if (st == NULL) {
01320 newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
01321 crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
01322 } else {
01323 SetDParam(1, st->index);
01324 newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
01325 crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
01326 }
01327
01328 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
01329
01330 AddNewsItem(newsitem,
01331 NS_ACCIDENT_VEHICLE,
01332 v->index,
01333 0);
01334
01335 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01336 }
01337
01338 static void MaybeCrashAirplane(Vehicle *v)
01339 {
01340 Station *st = GetStation(v->u.air.targetairport);
01341
01342
01343 uint16 prob = 0x10000 / 1500;
01344 if (st->Airport()->flags & AirportFTAClass::SHORT_STRIP &&
01345 AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
01346 !_cheats.no_jetcrash.value) {
01347 prob = 0x10000 / 20;
01348 }
01349
01350 if (GB(Random(), 0, 16) > prob) return;
01351
01352
01353 for (CargoID i = 0; i < NUM_CARGO; i++) {
01354 st->goods[i].rating = 1;
01355 st->goods[i].cargo.Truncate(0);
01356 }
01357
01358 CrashAirplane(v);
01359 }
01360
01362 static void AircraftEntersTerminal(Vehicle *v)
01363 {
01364 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01365
01366 Station *st = GetStation(v->u.air.targetairport);
01367 v->last_station_visited = v->u.air.targetairport;
01368
01369
01370 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01371 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01372 SetDParam(0, st->index);
01373
01374 AddNewsItem(
01375 STR_A033_CITIZENS_CELEBRATE_FIRST,
01376 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01377 v->index,
01378 st->index
01379 );
01380 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01381 }
01382
01383 v->BeginLoading();
01384 }
01385
01386 static void AircraftLandAirplane(Vehicle *v)
01387 {
01388 v->UpdateDeltaXY(INVALID_DIR);
01389
01390 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01391 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01392 }
01393 MaybeCrashAirplane(v);
01394 }
01395
01396
01398 void AircraftNextAirportPos_and_Order(Vehicle *v)
01399 {
01400 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01401 v->u.air.targetairport = v->current_order.GetDestination();
01402 }
01403
01404 const Station *st = GetTargetAirportIfValid(v);
01405 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01406 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, apc);
01407 }
01408
01409 void AircraftLeaveHangar(Vehicle *v)
01410 {
01411 v->cur_speed = 0;
01412 v->subspeed = 0;
01413 v->progress = 0;
01414 v->direction = DIR_SE;
01415 v->vehstatus &= ~VS_HIDDEN;
01416 {
01417 Vehicle *u = v->Next();
01418 u->vehstatus &= ~VS_HIDDEN;
01419
01420
01421 u = u->Next();
01422 if (u != NULL) {
01423 u->vehstatus &= ~VS_HIDDEN;
01424 u->cur_speed = 80;
01425 }
01426 }
01427
01428 VehicleServiceInDepot(v);
01429 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01430 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01431 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01432 }
01433
01438 static inline bool CheckSendAircraftToHangarForReplacement(const Vehicle *v)
01439 {
01440 EngineID new_engine;
01441 Company *c = GetCompany(v->owner);
01442
01443 if (VehicleHasDepotOrders(v)) return false;
01444
01445 new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
01446
01447 if (new_engine == INVALID_ENGINE) {
01448
01449 new_engine = v->engine_type;
01450
01451 if (!v->NeedsAutorenewing(c)) {
01452
01453 return false;
01454 }
01455 }
01456
01457 if (!HasBit(GetEngine(new_engine)->company_avail, v->owner)) {
01458
01459 return false;
01460 }
01461
01462 if (c->money < (c->engine_renew_money + (2 * DoCommand(0, new_engine, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT).GetCost()))) {
01463
01464
01465
01466
01467 return false;
01468 }
01469
01470
01471 return true;
01472 }
01473
01477 static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
01478 {
01479 AircraftEntersTerminal(v);
01480 v->u.air.state = apc->layout[v->u.air.pos].heading;
01481 }
01482
01483 static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
01484 {
01485 VehicleEnterDepot(v);
01486 v->u.air.state = apc->layout[v->u.air.pos].heading;
01487 }
01488
01490 static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
01491 {
01492
01493 if (v->u.air.previous_pos != v->u.air.pos) {
01494 AircraftEventHandler_EnterHangar(v, apc);
01495 return;
01496 }
01497
01498
01499 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01500 v->current_order.Free();
01501 return;
01502 }
01503
01504 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01505 !v->current_order.IsType(OT_GOTO_DEPOT))
01506 return;
01507
01508
01509 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01510
01511
01512 if (v->current_order.GetDestination() == v->u.air.targetairport) {
01513
01514
01515 if (v->subtype == AIR_HELICOPTER) {
01516 if (!AirportFindFreeHelipad(v, apc)) return;
01517 } else {
01518 if (!AirportFindFreeTerminal(v, apc)) return;
01519 }
01520 } else {
01521
01522 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01523 }
01524 AircraftLeaveHangar(v);
01525 AirportMove(v, apc);
01526 }
01527
01529 static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
01530 {
01531
01532 if (v->u.air.previous_pos != v->u.air.pos) {
01533 AircraftEventHandler_EnterTerminal(v, apc);
01534
01535
01536 if (_settings_game.order.serviceathelipad) {
01537 if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01538
01539 v->date_of_last_service = _date;
01540 v->breakdowns_since_last_service = 0;
01541 v->reliability = GetEngine(v->engine_type)->reliability;
01542 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01543 }
01544 }
01545 return;
01546 }
01547
01548 if (!v->current_order.IsValid()) return;
01549
01550
01551 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01552
01553
01554
01555
01556 bool go_to_hangar = false;
01557 switch (v->current_order.GetType()) {
01558 case OT_GOTO_STATION:
01559 break;
01560 case OT_GOTO_DEPOT:
01561 go_to_hangar = v->current_order.GetDestination() == v->u.air.targetairport;
01562 break;
01563 case OT_CONDITIONAL:
01564
01565
01566
01567 return;
01568 default:
01569 v->current_order.Free();
01570 go_to_hangar = GetStation(v->u.air.targetairport)->Airport()->nof_depots != 0;
01571 }
01572
01573 if (go_to_hangar) {
01574 v->u.air.state = HANGAR;
01575 } else {
01576
01577 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01578 }
01579 AirportMove(v, apc);
01580 }
01581
01582 static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
01583 {
01584 assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
01585 }
01586
01587 static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc)
01588 {
01589 PlayAircraftSound(v);
01590 v->u.air.state = STARTTAKEOFF;
01591 }
01592
01593 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
01594 {
01595 v->u.air.state = ENDTAKEOFF;
01596 v->UpdateDeltaXY(INVALID_DIR);
01597 }
01598
01599 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
01600 {
01601 v->u.air.state = FLYING;
01602
01603 AircraftNextAirportPos_and_Order(v);
01604 }
01605
01606 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
01607 {
01608 v->u.air.state = FLYING;
01609 v->UpdateDeltaXY(INVALID_DIR);
01610
01611
01612 AircraftNextAirportPos_and_Order(v);
01613
01614
01615 if (CheckSendAircraftToHangarForReplacement(v)) {
01616 _current_company = v->owner;
01617 DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01618 _current_company = OWNER_NONE;
01619 }
01620 }
01621
01622 static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
01623 {
01624 Station *st = GetStation(v->u.air.targetairport);
01625
01626
01627 if (apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES) &&
01628 st->airport_tile != INVALID_TILE &&
01629 (st->owner == OWNER_NONE || st->owner == v->owner)) {
01630
01631
01632
01633 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01634 const AirportFTA *current = apc->layout[v->u.air.pos].next;
01635 while (current != NULL) {
01636 if (current->heading == landingtype) {
01637
01638
01639
01640 uint16 tcur_speed = v->cur_speed;
01641 uint16 tsubspeed = v->subspeed;
01642 if (!AirportHasBlock(v, current, apc)) {
01643 v->u.air.state = landingtype;
01644
01645
01646
01647 v->u.air.pos = current->next_position;
01648 SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
01649 return;
01650 }
01651 v->cur_speed = tcur_speed;
01652 v->subspeed = tsubspeed;
01653 }
01654 current = current->next;
01655 }
01656 }
01657 v->u.air.state = FLYING;
01658 v->u.air.pos = apc->layout[v->u.air.pos].next_position;
01659 }
01660
01661 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
01662 {
01663 v->u.air.state = ENDLANDING;
01664 AircraftLandAirplane(v);
01665
01666
01667 if (CheckSendAircraftToHangarForReplacement(v)) {
01668 _current_company = v->owner;
01669 DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01670 _current_company = OWNER_NONE;
01671 }
01672 }
01673
01674 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
01675 {
01676 v->u.air.state = HELIENDLANDING;
01677 v->UpdateDeltaXY(INVALID_DIR);
01678 }
01679
01680 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
01681 {
01682
01683 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01684
01685
01686
01687
01688
01689 if (v->current_order.IsType(OT_GOTO_STATION)) {
01690 if (AirportFindFreeTerminal(v, apc)) return;
01691 }
01692 v->u.air.state = HANGAR;
01693
01694 }
01695
01696 static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
01697 {
01698
01699 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01700
01701
01702
01703
01704
01705
01706
01707
01708 if (v->current_order.IsType(OT_GOTO_STATION)) {
01709 if (AirportFindFreeHelipad(v, apc)) return;
01710 }
01711 v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01712 }
01713
01714 typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
01715 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01716 AircraftEventHandler_General,
01717 AircraftEventHandler_InHangar,
01718 AircraftEventHandler_AtTerminal,
01719 AircraftEventHandler_AtTerminal,
01720 AircraftEventHandler_AtTerminal,
01721 AircraftEventHandler_AtTerminal,
01722 AircraftEventHandler_AtTerminal,
01723 AircraftEventHandler_AtTerminal,
01724 AircraftEventHandler_AtTerminal,
01725 AircraftEventHandler_AtTerminal,
01726 AircraftEventHandler_TakeOff,
01727 AircraftEventHandler_StartTakeOff,
01728 AircraftEventHandler_EndTakeOff,
01729 AircraftEventHandler_HeliTakeOff,
01730 AircraftEventHandler_Flying,
01731 AircraftEventHandler_Landing,
01732 AircraftEventHandler_EndLanding,
01733 AircraftEventHandler_HeliLanding,
01734 AircraftEventHandler_HeliEndLanding,
01735 AircraftEventHandler_AtTerminal,
01736 AircraftEventHandler_AtTerminal,
01737 AircraftEventHandler_AtTerminal,
01738 AircraftEventHandler_AtTerminal,
01739 };
01740
01741 static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
01742 {
01743
01744 if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
01745 Station *st = GetStation(v->u.air.targetairport);
01746
01747 CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
01748 }
01749 }
01750
01751 static void AirportGoToNextPosition(Vehicle *v)
01752 {
01753
01754 if (!AircraftController(v)) return;
01755
01756 const AirportFTAClass *apc = GetStation(v->u.air.targetairport)->Airport();
01757
01758 AirportClearBlock(v, apc);
01759 AirportMove(v, apc);
01760 }
01761
01762
01763 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
01764 {
01765
01766 if (v->u.air.pos >= apc->nofelements) {
01767 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
01768 assert(v->u.air.pos < apc->nofelements);
01769 }
01770
01771 const AirportFTA *current = &apc->layout[v->u.air.pos];
01772
01773 if (current->heading == v->u.air.state) {
01774 byte prev_pos = v->u.air.pos;
01775 byte prev_state = v->u.air.state;
01776 _aircraft_state_handlers[v->u.air.state](v, apc);
01777 if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
01778 if (v->u.air.state != prev_state || v->u.air.pos != prev_pos) UpdateAircraftCache(v);
01779 return true;
01780 }
01781
01782 v->u.air.previous_pos = v->u.air.pos;
01783
01784
01785 if (current->next == NULL) {
01786 if (AirportSetBlocks(v, current, apc)) {
01787 v->u.air.pos = current->next_position;
01788 UpdateAircraftCache(v);
01789 }
01790 return false;
01791 }
01792
01793
01794
01795 do {
01796 if (v->u.air.state == current->heading || current->heading == TO_ALL) {
01797 if (AirportSetBlocks(v, current, apc)) {
01798 v->u.air.pos = current->next_position;
01799 UpdateAircraftCache(v);
01800 }
01801 return false;
01802 }
01803 current = current->next;
01804 } while (current != NULL);
01805
01806 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);
01807 assert(0);
01808 return false;
01809 }
01810
01811
01812 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01813 {
01814 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01815 const AirportFTA *next = &apc->layout[current_pos->next_position];
01816
01817
01818 if (apc->layout[current_pos->position].block != next->block) {
01819 const Station *st = GetStation(v->u.air.targetairport);
01820 uint64 airport_flags = next->block;
01821
01822
01823 if (current_pos != reference && current_pos->block != NOTHING_block) {
01824 airport_flags |= current_pos->block;
01825 }
01826
01827 if (HASBITS(st->airport_flags, airport_flags)) {
01828 v->cur_speed = 0;
01829 v->subspeed = 0;
01830 return true;
01831 }
01832 }
01833 return false;
01834 }
01835
01843 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01844 {
01845 const AirportFTA *next = &apc->layout[current_pos->next_position];
01846 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01847
01848
01849 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01850 uint64 airport_flags = next->block;
01851
01852
01853 const AirportFTA *current = current_pos;
01854 if (current == reference) current = current->next;
01855 while (current != NULL) {
01856 if (current->heading == current_pos->heading && current->block != 0) {
01857 airport_flags |= current->block;
01858 break;
01859 }
01860 current = current->next;
01861 };
01862
01863
01864
01865 if (current_pos->block == next->block) airport_flags ^= next->block;
01866
01867 Station *st = GetStation(v->u.air.targetairport);
01868 if (HASBITS(st->airport_flags, airport_flags)) {
01869 v->cur_speed = 0;
01870 v->subspeed = 0;
01871 return false;
01872 }
01873
01874 if (next->block != NOTHING_block) {
01875 SETBITS(st->airport_flags, airport_flags);
01876 }
01877 }
01878 return true;
01879 }
01880
01881 static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
01882 {
01883 Station *st = GetStation(v->u.air.targetairport);
01884 for (; i < last_terminal; i++) {
01885 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01886
01887 v->u.air.state = _airport_terminal_state[i];
01888 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01889 return true;
01890 }
01891 }
01892 return false;
01893 }
01894
01895 static uint GetNumTerminals(const AirportFTAClass *apc)
01896 {
01897 uint num = 0;
01898
01899 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01900
01901 return num;
01902 }
01903
01904 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
01905 {
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916 if (apc->terminals[0] > 1) {
01917 const Station *st = GetStation(v->u.air.targetairport);
01918 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01919
01920 while (temp != NULL) {
01921 if (temp->heading == 255) {
01922 if (!HASBITS(st->airport_flags, temp->block)) {
01923
01924
01925 uint target_group = temp->next_position + 1;
01926
01927
01928
01929
01930 uint group_start = 0;
01931 for (uint i = 1; i < target_group; i++) {
01932 group_start += apc->terminals[i];
01933 }
01934
01935 uint group_end = group_start + apc->terminals[target_group];
01936 if (FreeTerminal(v, group_start, group_end)) return true;
01937 }
01938 } else {
01939
01940
01941 return false;
01942 }
01943 temp = temp->next;
01944 }
01945 }
01946
01947
01948 return FreeTerminal(v, 0, GetNumTerminals(apc));
01949 }
01950
01951 static uint GetNumHelipads(const AirportFTAClass *apc)
01952 {
01953 uint num = 0;
01954
01955 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01956
01957 return num;
01958 }
01959
01960
01961 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
01962 {
01963
01964 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01965
01966
01967 if (apc->helipads[0] > 1) {
01968 const Station *st = GetStation(v->u.air.targetairport);
01969 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01970
01971 while (temp != NULL) {
01972 if (temp->heading == 255) {
01973 if (!HASBITS(st->airport_flags, temp->block)) {
01974
01975
01976
01977 uint target_group = temp->next_position + 1;
01978
01979
01980
01981
01982 uint group_start = 0;
01983 for (uint i = 1; i < target_group; i++) {
01984 group_start += apc->helipads[i];
01985 }
01986
01987 uint group_end = group_start + apc->helipads[target_group];
01988 if (FreeTerminal(v, group_start, group_end)) return true;
01989 }
01990 } else {
01991
01992
01993 return false;
01994 }
01995 temp = temp->next;
01996 }
01997 } else {
01998
01999
02000 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
02001 }
02002 return false;
02003 }
02004
02005 static void AircraftEventHandler(Vehicle *v, int loop)
02006 {
02007 v->tick_counter++;
02008
02009 if (v->vehstatus & VS_CRASHED) {
02010 HandleCrashedAircraft(v);
02011 return;
02012 }
02013
02014 if (v->vehstatus & VS_STOPPED) return;
02015
02016
02017 if (v->breakdown_ctr != 0) {
02018 if (v->breakdown_ctr <= 2) {
02019 HandleBrokenAircraft(v);
02020 } else {
02021 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
02022 }
02023 }
02024
02025 HandleAircraftSmoke(v);
02026 ProcessOrders(v);
02027 v->HandleLoading(loop != 0);
02028
02029 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return;
02030
02031 AirportGoToNextPosition(v);
02032 }
02033
02034 void Aircraft::Tick()
02035 {
02036 if (!IsNormalAircraft(this)) return;
02037
02038 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
02039
02040 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
02041
02042 AgeAircraftCargo(this);
02043
02044 this->current_order_time++;
02045
02046 for (uint i = 0; i != 2; i++) {
02047 AircraftEventHandler(this, i);
02048 if (this->type != VEH_AIRCRAFT)
02049 break;
02050 }
02051 }
02052
02053
02059 Station *GetTargetAirportIfValid(const Vehicle *v)
02060 {
02061 assert(v->type == VEH_AIRCRAFT);
02062
02063 StationID sid = v->u.air.targetairport;
02064
02065 if (!IsValidStationID(sid)) return NULL;
02066
02067 Station *st = GetStation(sid);
02068
02069 return st->airport_tile == INVALID_TILE ? NULL : st;
02070 }
02071
02076 void UpdateAirplanesOnNewStation(const Station *st)
02077 {
02078
02079 const AirportFTAClass *ap = st->Airport();
02080
02081 Vehicle *v;
02082 FOR_ALL_VEHICLES(v) {
02083 if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
02084 if (v->u.air.targetairport == st->index) {
02085
02086
02087 if (v->u.air.state >= FLYING) {
02088 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, ap);
02089 v->u.air.state = FLYING;
02090 UpdateAircraftCache(v);
02091
02092
02093 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
02094
02095 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02096 } else {
02097 assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF);
02098 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02099
02100
02101 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02102 if (ap->layout[cnt].heading == takeofftype) {
02103 v->u.air.pos = ap->layout[cnt].position;
02104 UpdateAircraftCache(v);
02105 break;
02106 }
02107 }
02108 }
02109 }
02110 }
02111 }
02112 }