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->UpdateViewport(true, false);
00710 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00711
00712 Vehicle *u = v->Next();
00713
00714 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00715 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00716 u->x_pos = x;
00717 u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
00718
00719 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00720 u->z_pos = GetSlopeZ(safe_x, safe_y);
00721 u->cur_image = v->cur_image;
00722
00723 VehicleMove(u, true);
00724
00725 u = u->Next();
00726 if (u != NULL) {
00727 u->x_pos = x;
00728 u->y_pos = y;
00729 u->z_pos = z + 5;
00730
00731 VehicleMove(u, true);
00732 }
00733 }
00734
00738 void HandleAircraftEnterHangar(Vehicle *v)
00739 {
00740 v->subspeed = 0;
00741 v->progress = 0;
00742
00743 Vehicle *u = v->Next();
00744 u->vehstatus |= VS_HIDDEN;
00745 u = u->Next();
00746 if (u != NULL) {
00747 u->vehstatus |= VS_HIDDEN;
00748 u->cur_speed = 0;
00749 }
00750
00751 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00752 }
00753
00754 static void PlayAircraftSound(const Vehicle *v)
00755 {
00756 if (!PlayVehicleSound(v, VSE_START)) {
00757 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00758 }
00759 }
00760
00761
00762 void UpdateAircraftCache(Vehicle *v)
00763 {
00764 uint max_speed = GetVehicleProperty(v, 0x0C, 0);
00765 if (max_speed != 0) {
00766
00767 max_speed = (max_speed * 129) / 10;
00768
00769 v->u.air.cached_max_speed = max_speed;
00770 } else {
00771 v->u.air.cached_max_speed = 0xFFFF;
00772 }
00773 }
00774
00775
00779 enum AircraftSpeedLimits {
00780 SPEED_LIMIT_TAXI = 50,
00781 SPEED_LIMIT_APPROACH = 230,
00782 SPEED_LIMIT_BROKEN = 320,
00783 SPEED_LIMIT_HOLD = 425,
00784 SPEED_LIMIT_NONE = 0xFFFF
00785 };
00786
00794 static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00795 {
00796 uint spd = v->acceleration * 16;
00797 byte t;
00798
00799
00800
00801 speed_limit *= _settings_game.vehicle.plane_speed;
00802
00803 if (v->u.air.cached_max_speed < speed_limit) {
00804 if (v->cur_speed < speed_limit) hard_limit = false;
00805 speed_limit = v->u.air.cached_max_speed;
00806 }
00807
00808 speed_limit = min(speed_limit, v->max_speed);
00809
00810 v->subspeed = (t=v->subspeed) + (byte)spd;
00811
00812
00813
00814
00815
00816
00817
00818 if (!hard_limit && v->cur_speed > speed_limit) {
00819 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00820 }
00821
00822 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00823
00824
00825 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00826
00827
00828 if (spd != v->cur_speed) {
00829 v->cur_speed = spd;
00830 if (_settings_client.gui.vehicle_speed)
00831 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00832 }
00833
00834
00835 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00836
00837 if (!(v->direction & 1)) spd = spd * 3 / 4;
00838
00839 spd += v->progress;
00840 v->progress = (byte)spd;
00841 return spd >> 8;
00842 }
00843
00851 byte GetAircraftFlyingAltitude(const Vehicle *v)
00852 {
00853
00854
00855
00856 byte base_altitude = 150;
00857
00858
00859
00860
00861 switch (v->direction) {
00862 case DIR_N:
00863 case DIR_NE:
00864 case DIR_E:
00865 case DIR_SE:
00866 base_altitude += 10;
00867 break;
00868
00869 default: break;
00870 }
00871
00872
00873 base_altitude += min(20 * (v->max_speed / 200), 90);
00874
00875 return base_altitude;
00876 }
00877
00891 static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
00892 {
00893 assert(v != NULL);
00894 assert(apc != NULL);
00895
00896
00897
00898
00899 TileIndex tile = 0;
00900
00901 if (IsValidStationID(v->u.air.targetairport)) {
00902 const Station *st = GetStation(v->u.air.targetairport);
00903
00904 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00905 }
00906
00907 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00908 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00909
00910 DiagDirection dir;
00911 if (abs(delta_y) < abs(delta_x)) {
00912
00913 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00914 } else {
00915
00916 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00917 }
00918 return apc->entry_points[dir];
00919 }
00920
00928 static bool AircraftController(Vehicle *v)
00929 {
00930 int count;
00931
00932
00933 const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00934
00935 TileIndex tile = INVALID_TILE;
00936 if (st != NULL) {
00937 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00938 }
00939
00940 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00941
00942
00943 if (st == NULL || st->airport_tile == INVALID_TILE) {
00944
00945 if (v->u.air.pos >= afc->nofelements) {
00946 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
00947 } else if (v->u.air.targetairport != v->current_order.GetDestination()) {
00948
00949 v->u.air.state = FLYING;
00950 UpdateAircraftCache(v);
00951 AircraftNextAirportPos_and_Order(v);
00952
00953 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00954 return false;
00955 }
00956 }
00957
00958
00959 const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
00960
00961 int x = TileX(tile) * TILE_SIZE;
00962 int y = TileY(tile) * TILE_SIZE;
00963
00964
00965 if (amd->flag & AMED_HELI_RAISE) {
00966 Vehicle *u = v->Next()->Next();
00967
00968
00969 if (u->cur_speed > 32) {
00970 v->cur_speed = 0;
00971 if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00972 } else {
00973 u->cur_speed = 32;
00974 count = UpdateAircraftSpeed(v);
00975 if (count > 0) {
00976 v->tile = 0;
00977
00978
00979 if (v->z_pos >= 184) {
00980 v->cur_speed = 0;
00981 return true;
00982 }
00983 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00984 }
00985 }
00986 return false;
00987 }
00988
00989
00990 if (amd->flag & AMED_HELI_LOWER) {
00991 if (st == NULL) {
00992
00993
00994
00995 v->u.air.state = FLYING;
00996 UpdateAircraftCache(v);
00997 AircraftNextAirportPos_and_Order(v);
00998 return false;
00999 }
01000
01001
01002 v->tile = tile;
01003
01004
01005 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
01006
01007 if (z == v->z_pos) {
01008 Vehicle *u = v->Next()->Next();
01009
01010
01011 if (u->cur_speed >= 80) return true;
01012 u->cur_speed += 4;
01013 } else {
01014 count = UpdateAircraftSpeed(v);
01015 if (count > 0) {
01016 if (v->z_pos > z) {
01017 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
01018 } else {
01019 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
01020 }
01021 }
01022 }
01023 return false;
01024 }
01025
01026
01027 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
01028
01029
01030 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01031
01032
01033 if (dist == 0) {
01034
01035 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01036
01037
01038 if (dirdiff == DIRDIFF_SAME) {
01039 v->cur_speed = 0;
01040 return true;
01041 }
01042
01043 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
01044
01045 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01046 v->cur_speed >>= 1;
01047
01048 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01049 return false;
01050 }
01051
01052 uint speed_limit = SPEED_LIMIT_TAXI;
01053 bool hard_limit = true;
01054
01055 if (amd->flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
01056 if (amd->flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
01057 if (amd->flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
01058 if (amd->flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
01059
01060 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
01061 if (count == 0) return false;
01062
01063 if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
01064
01065 do {
01066
01067 GetNewVehiclePosResult gp;
01068
01069 if (dist < 4 || amd->flag & AMED_LAND) {
01070
01071 gp.x = (v->x_pos != (x + amd->x)) ?
01072 v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
01073 v->x_pos;
01074 gp.y = (v->y_pos != (y + amd->y)) ?
01075 v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01076 v->y_pos;
01077
01078
01079 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01080
01081 } else {
01082
01083
01084 Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01085 if (newdir != v->direction) {
01086 v->direction = newdir;
01087 if (amd->flag & AMED_SLOWTURN) {
01088 if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
01089 } else {
01090 v->cur_speed >>= 1;
01091 }
01092 }
01093
01094
01095 gp = GetNewVehiclePos(v);
01096 }
01097
01098 v->tile = gp.new_tile;
01099
01100 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01101
01102
01103 uint z = v->z_pos;
01104
01105 if (amd->flag & AMED_TAKEOFF) {
01106 z = min(z + 2, GetAircraftFlyingAltitude(v));
01107 }
01108
01109 if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01110
01111 if (amd->flag & AMED_LAND) {
01112 if (st->airport_tile == INVALID_TILE) {
01113
01114 v->u.air.state = FLYING;
01115 UpdateAircraftCache(v);
01116 AircraftNextAirportPos_and_Order(v);
01117
01118 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01119 continue;
01120 }
01121
01122 uint curz = GetSlopeZ(x, y) + 1;
01123
01124 if (curz > z) {
01125 z++;
01126 } else {
01127 int t = max(1U, dist - 4);
01128
01129 z -= ((z - curz) + t - 1) / t;
01130 if (z < curz) z = curz;
01131 }
01132 }
01133
01134
01135 if (amd->flag & AMED_BRAKE) {
01136 uint curz = GetSlopeZ(x, y) + 1;
01137
01138 if (z > curz) {
01139 z--;
01140 } else if (z < curz) {
01141 z++;
01142 }
01143
01144 }
01145
01146 SetAircraftPosition(v, gp.x, gp.y, z);
01147 } while (--count != 0);
01148 return false;
01149 }
01150
01151
01152 static void HandleCrashedAircraft(Vehicle *v)
01153 {
01154 v->u.air.crashed_counter += 3;
01155
01156 Station *st = GetTargetAirportIfValid(v);
01157
01158
01159 if (v->u.air.crashed_counter < 500 && st == NULL && ((v->u.air.crashed_counter % 3) == 0) ) {
01160 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01161 v->z_pos -= 1;
01162 if (v->z_pos == z) {
01163 v->u.air.crashed_counter = 500;
01164 v->z_pos++;
01165 }
01166 }
01167
01168 if (v->u.air.crashed_counter < 650) {
01169 uint32 r;
01170 if (Chance16R(1,32,r)) {
01171 static const DirDiff delta[] = {
01172 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01173 };
01174
01175 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01176 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01177 r = Random();
01178 CreateEffectVehicleRel(v,
01179 GB(r, 0, 4) - 4,
01180 GB(r, 4, 4) - 4,
01181 GB(r, 8, 4),
01182 EV_EXPLOSION_SMALL);
01183 }
01184 } else if (v->u.air.crashed_counter >= 10000) {
01185
01186
01187
01188
01189
01190 if (st != NULL) {
01191 CLRBITS(st->airport_flags, RUNWAY_IN_block);
01192 CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block);
01193 CLRBITS(st->airport_flags, RUNWAY_IN2_block);
01194 }
01195
01196 delete v;
01197 }
01198 }
01199
01200 static void HandleBrokenAircraft(Vehicle *v)
01201 {
01202 if (v->breakdown_ctr != 1) {
01203 v->breakdown_ctr = 1;
01204 v->vehstatus |= VS_AIRCRAFT_BROKEN;
01205
01206 if (v->breakdowns_since_last_service != 255)
01207 v->breakdowns_since_last_service++;
01208 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01209 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01210 }
01211 }
01212
01213
01214 static void HandleAircraftSmoke(Vehicle *v)
01215 {
01216 static const struct {
01217 int8 x;
01218 int8 y;
01219 } smoke_pos[] = {
01220 { 5, 5 },
01221 { 6, 0 },
01222 { 5, -5 },
01223 { 0, -6 },
01224 { -5, -5 },
01225 { -6, 0 },
01226 { -5, 5 },
01227 { 0, 6 }
01228 };
01229
01230 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01231
01232 if (v->cur_speed < 10) {
01233 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01234 v->breakdown_ctr = 0;
01235 return;
01236 }
01237
01238 if ((v->tick_counter & 0x1F) == 0) {
01239 CreateEffectVehicleRel(v,
01240 smoke_pos[v->direction].x,
01241 smoke_pos[v->direction].y,
01242 2,
01243 EV_SMOKE
01244 );
01245 }
01246 }
01247
01248 void HandleMissingAircraftOrders(Vehicle *v)
01249 {
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265 const Station *st = GetTargetAirportIfValid(v);
01266 if (st == NULL) {
01267 CommandCost ret;
01268 CompanyID old_company = _current_company;
01269
01270 _current_company = v->owner;
01271 ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01272 _current_company = old_company;
01273
01274 if (CmdFailed(ret)) CrashAirplane(v);
01275 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01276 v->current_order.Free();
01277 }
01278 }
01279
01280
01281 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01282 {
01283
01284 if (this->u.air.state == FLYING) {
01285 AircraftNextAirportPos_and_Order(this);
01286 }
01287
01288
01289 return 0;
01290 }
01291
01292 void Aircraft::MarkDirty()
01293 {
01294 this->UpdateViewport(false, false);
01295 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01296 }
01297
01298 static void CrashAirplane(Vehicle *v)
01299 {
01300 v->vehstatus |= VS_CRASHED;
01301 v->u.air.crashed_counter = 0;
01302
01303 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01304
01305 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01306 v->MarkDirty();
01307
01308 uint amt = 2;
01309 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count();
01310 SetDParam(0, amt);
01311
01312 v->cargo.Truncate(0);
01313 v->Next()->cargo.Truncate(0);
01314 const Station *st = GetTargetAirportIfValid(v);
01315 StringID newsitem;
01316 AIEventVehicleCrashed::CrashReason crash_reason;
01317 if (st == NULL) {
01318 newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
01319 crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
01320 } else {
01321 SetDParam(1, st->index);
01322 newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
01323 crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
01324 }
01325
01326 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
01327
01328 AddNewsItem(newsitem,
01329 NS_ACCIDENT_VEHICLE,
01330 v->index,
01331 0);
01332
01333 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01334 }
01335
01336 static void MaybeCrashAirplane(Vehicle *v)
01337 {
01338 Station *st = GetStation(v->u.air.targetairport);
01339
01340
01341 uint16 prob = 0x10000 / 1500;
01342 if (st->Airport()->flags & AirportFTAClass::SHORT_STRIP &&
01343 AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
01344 !_cheats.no_jetcrash.value) {
01345 prob = 0x10000 / 20;
01346 }
01347
01348 if (GB(Random(), 0, 16) > prob) return;
01349
01350
01351 for (CargoID i = 0; i < NUM_CARGO; i++) {
01352 st->goods[i].rating = 1;
01353 st->goods[i].cargo.Truncate(0);
01354 }
01355
01356 CrashAirplane(v);
01357 }
01358
01360 static void AircraftEntersTerminal(Vehicle *v)
01361 {
01362 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01363
01364 Station *st = GetStation(v->u.air.targetairport);
01365 v->last_station_visited = v->u.air.targetairport;
01366
01367
01368 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01369 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01370 SetDParam(0, st->index);
01371
01372 AddNewsItem(
01373 STR_A033_CITIZENS_CELEBRATE_FIRST,
01374 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01375 v->index,
01376 st->index
01377 );
01378 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01379 }
01380
01381 v->BeginLoading();
01382 }
01383
01384 static void AircraftLandAirplane(Vehicle *v)
01385 {
01386 v->UpdateDeltaXY(INVALID_DIR);
01387
01388 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01389 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01390 }
01391 MaybeCrashAirplane(v);
01392 }
01393
01394
01396 void AircraftNextAirportPos_and_Order(Vehicle *v)
01397 {
01398 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01399 v->u.air.targetairport = v->current_order.GetDestination();
01400 }
01401
01402 const Station *st = GetTargetAirportIfValid(v);
01403 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01404 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, apc);
01405 }
01406
01407 void AircraftLeaveHangar(Vehicle *v)
01408 {
01409 v->cur_speed = 0;
01410 v->subspeed = 0;
01411 v->progress = 0;
01412 v->direction = DIR_SE;
01413 v->vehstatus &= ~VS_HIDDEN;
01414 {
01415 Vehicle *u = v->Next();
01416 u->vehstatus &= ~VS_HIDDEN;
01417
01418
01419 u = u->Next();
01420 if (u != NULL) {
01421 u->vehstatus &= ~VS_HIDDEN;
01422 u->cur_speed = 80;
01423 }
01424 }
01425
01426 VehicleServiceInDepot(v);
01427 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01428 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01429 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01430 }
01431
01436 static inline bool CheckSendAircraftToHangarForReplacement(const Vehicle *v)
01437 {
01438 EngineID new_engine;
01439 Company *c = GetCompany(v->owner);
01440
01441 if (VehicleHasDepotOrders(v)) return false;
01442
01443 new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
01444
01445 if (new_engine == INVALID_ENGINE) {
01446
01447 new_engine = v->engine_type;
01448
01449 if (!v->NeedsAutorenewing(c)) {
01450
01451 return false;
01452 }
01453 }
01454
01455 if (!HasBit(GetEngine(new_engine)->company_avail, v->owner)) {
01456
01457 return false;
01458 }
01459
01460 if (c->money < (c->engine_renew_money + (2 * DoCommand(0, new_engine, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT).GetCost()))) {
01461
01462
01463
01464
01465 return false;
01466 }
01467
01468
01469 return true;
01470 }
01471
01475 static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
01476 {
01477 AircraftEntersTerminal(v);
01478 v->u.air.state = apc->layout[v->u.air.pos].heading;
01479 }
01480
01481 static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
01482 {
01483 VehicleEnterDepot(v);
01484 v->u.air.state = apc->layout[v->u.air.pos].heading;
01485 }
01486
01488 static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
01489 {
01490
01491 if (v->u.air.previous_pos != v->u.air.pos) {
01492 AircraftEventHandler_EnterHangar(v, apc);
01493 return;
01494 }
01495
01496
01497 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01498 v->current_order.Free();
01499 return;
01500 }
01501
01502 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01503 !v->current_order.IsType(OT_GOTO_DEPOT))
01504 return;
01505
01506
01507 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01508
01509
01510 if (v->current_order.GetDestination() == v->u.air.targetairport) {
01511
01512
01513 if (v->subtype == AIR_HELICOPTER) {
01514 if (!AirportFindFreeHelipad(v, apc)) return;
01515 } else {
01516 if (!AirportFindFreeTerminal(v, apc)) return;
01517 }
01518 } else {
01519
01520 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01521 }
01522 AircraftLeaveHangar(v);
01523 AirportMove(v, apc);
01524 }
01525
01527 static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
01528 {
01529
01530 if (v->u.air.previous_pos != v->u.air.pos) {
01531 AircraftEventHandler_EnterTerminal(v, apc);
01532
01533
01534 if (_settings_game.order.serviceathelipad) {
01535 if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01536
01537 v->date_of_last_service = _date;
01538 v->breakdowns_since_last_service = 0;
01539 v->reliability = GetEngine(v->engine_type)->reliability;
01540 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01541 }
01542 }
01543 return;
01544 }
01545
01546 if (!v->current_order.IsValid()) return;
01547
01548
01549 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01550
01551
01552
01553
01554 bool go_to_hangar = false;
01555 switch (v->current_order.GetType()) {
01556 case OT_GOTO_STATION:
01557 break;
01558 case OT_GOTO_DEPOT:
01559 go_to_hangar = v->current_order.GetDestination() == v->u.air.targetairport;
01560 break;
01561 case OT_CONDITIONAL:
01562
01563
01564
01565 return;
01566 default:
01567 v->current_order.Free();
01568 go_to_hangar = GetStation(v->u.air.targetairport)->Airport()->nof_depots != 0;
01569 }
01570
01571 if (go_to_hangar) {
01572 v->u.air.state = HANGAR;
01573 } else {
01574
01575 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01576 }
01577 AirportMove(v, apc);
01578 }
01579
01580 static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
01581 {
01582 assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
01583 }
01584
01585 static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc)
01586 {
01587 PlayAircraftSound(v);
01588 v->u.air.state = STARTTAKEOFF;
01589 }
01590
01591 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
01592 {
01593 v->u.air.state = ENDTAKEOFF;
01594 v->UpdateDeltaXY(INVALID_DIR);
01595 }
01596
01597 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
01598 {
01599 v->u.air.state = FLYING;
01600
01601 AircraftNextAirportPos_and_Order(v);
01602 }
01603
01604 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
01605 {
01606 v->u.air.state = FLYING;
01607 v->UpdateDeltaXY(INVALID_DIR);
01608
01609
01610 AircraftNextAirportPos_and_Order(v);
01611
01612
01613 if (CheckSendAircraftToHangarForReplacement(v)) {
01614 _current_company = v->owner;
01615 DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01616 _current_company = OWNER_NONE;
01617 }
01618 }
01619
01620 static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
01621 {
01622 Station *st = GetStation(v->u.air.targetairport);
01623
01624
01625 if (apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES) &&
01626 st->airport_tile != INVALID_TILE &&
01627 (st->owner == OWNER_NONE || st->owner == v->owner)) {
01628
01629
01630
01631 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01632 const AirportFTA *current = apc->layout[v->u.air.pos].next;
01633 while (current != NULL) {
01634 if (current->heading == landingtype) {
01635
01636
01637
01638 uint16 tcur_speed = v->cur_speed;
01639 uint16 tsubspeed = v->subspeed;
01640 if (!AirportHasBlock(v, current, apc)) {
01641 v->u.air.state = landingtype;
01642
01643
01644
01645 v->u.air.pos = current->next_position;
01646 SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
01647 return;
01648 }
01649 v->cur_speed = tcur_speed;
01650 v->subspeed = tsubspeed;
01651 }
01652 current = current->next;
01653 }
01654 }
01655 v->u.air.state = FLYING;
01656 v->u.air.pos = apc->layout[v->u.air.pos].next_position;
01657 }
01658
01659 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
01660 {
01661 v->u.air.state = ENDLANDING;
01662 AircraftLandAirplane(v);
01663
01664
01665 if (CheckSendAircraftToHangarForReplacement(v)) {
01666 _current_company = v->owner;
01667 DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01668 _current_company = OWNER_NONE;
01669 }
01670 }
01671
01672 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
01673 {
01674 v->u.air.state = HELIENDLANDING;
01675 v->UpdateDeltaXY(INVALID_DIR);
01676 }
01677
01678 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
01679 {
01680
01681 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01682
01683
01684
01685
01686
01687 if (v->current_order.IsType(OT_GOTO_STATION)) {
01688 if (AirportFindFreeTerminal(v, apc)) return;
01689 }
01690 v->u.air.state = HANGAR;
01691
01692 }
01693
01694 static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
01695 {
01696
01697 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01698
01699
01700
01701
01702
01703
01704
01705
01706 if (v->current_order.IsType(OT_GOTO_STATION)) {
01707 if (AirportFindFreeHelipad(v, apc)) return;
01708 }
01709 v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01710 }
01711
01712 typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
01713 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01714 AircraftEventHandler_General,
01715 AircraftEventHandler_InHangar,
01716 AircraftEventHandler_AtTerminal,
01717 AircraftEventHandler_AtTerminal,
01718 AircraftEventHandler_AtTerminal,
01719 AircraftEventHandler_AtTerminal,
01720 AircraftEventHandler_AtTerminal,
01721 AircraftEventHandler_AtTerminal,
01722 AircraftEventHandler_AtTerminal,
01723 AircraftEventHandler_AtTerminal,
01724 AircraftEventHandler_TakeOff,
01725 AircraftEventHandler_StartTakeOff,
01726 AircraftEventHandler_EndTakeOff,
01727 AircraftEventHandler_HeliTakeOff,
01728 AircraftEventHandler_Flying,
01729 AircraftEventHandler_Landing,
01730 AircraftEventHandler_EndLanding,
01731 AircraftEventHandler_HeliLanding,
01732 AircraftEventHandler_HeliEndLanding,
01733 AircraftEventHandler_AtTerminal,
01734 AircraftEventHandler_AtTerminal,
01735 AircraftEventHandler_AtTerminal,
01736 AircraftEventHandler_AtTerminal,
01737 };
01738
01739 static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
01740 {
01741
01742 if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
01743 Station *st = GetStation(v->u.air.targetairport);
01744
01745 CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
01746 }
01747 }
01748
01749 static void AirportGoToNextPosition(Vehicle *v)
01750 {
01751
01752 if (!AircraftController(v)) return;
01753
01754 const AirportFTAClass *apc = GetStation(v->u.air.targetairport)->Airport();
01755
01756 AirportClearBlock(v, apc);
01757 AirportMove(v, apc);
01758 }
01759
01760
01761 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
01762 {
01763
01764 if (v->u.air.pos >= apc->nofelements) {
01765 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
01766 assert(v->u.air.pos < apc->nofelements);
01767 }
01768
01769 const AirportFTA *current = &apc->layout[v->u.air.pos];
01770
01771 if (current->heading == v->u.air.state) {
01772 byte prev_pos = v->u.air.pos;
01773 byte prev_state = v->u.air.state;
01774 _aircraft_state_handlers[v->u.air.state](v, apc);
01775 if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
01776 if (v->u.air.state != prev_state || v->u.air.pos != prev_pos) UpdateAircraftCache(v);
01777 return true;
01778 }
01779
01780 v->u.air.previous_pos = v->u.air.pos;
01781
01782
01783 if (current->next == NULL) {
01784 if (AirportSetBlocks(v, current, apc)) {
01785 v->u.air.pos = current->next_position;
01786 UpdateAircraftCache(v);
01787 }
01788 return false;
01789 }
01790
01791
01792
01793 do {
01794 if (v->u.air.state == current->heading || current->heading == TO_ALL) {
01795 if (AirportSetBlocks(v, current, apc)) {
01796 v->u.air.pos = current->next_position;
01797 UpdateAircraftCache(v);
01798 }
01799 return false;
01800 }
01801 current = current->next;
01802 } while (current != NULL);
01803
01804 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);
01805 assert(0);
01806 return false;
01807 }
01808
01809
01810 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01811 {
01812 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01813 const AirportFTA *next = &apc->layout[current_pos->next_position];
01814
01815
01816 if (apc->layout[current_pos->position].block != next->block) {
01817 const Station *st = GetStation(v->u.air.targetairport);
01818 uint64 airport_flags = next->block;
01819
01820
01821 if (current_pos != reference && current_pos->block != NOTHING_block) {
01822 airport_flags |= current_pos->block;
01823 }
01824
01825 if (HASBITS(st->airport_flags, airport_flags)) {
01826 v->cur_speed = 0;
01827 v->subspeed = 0;
01828 return true;
01829 }
01830 }
01831 return false;
01832 }
01833
01841 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01842 {
01843 const AirportFTA *next = &apc->layout[current_pos->next_position];
01844 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01845
01846
01847 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01848 uint64 airport_flags = next->block;
01849
01850
01851 const AirportFTA *current = current_pos;
01852 if (current == reference) current = current->next;
01853 while (current != NULL) {
01854 if (current->heading == current_pos->heading && current->block != 0) {
01855 airport_flags |= current->block;
01856 break;
01857 }
01858 current = current->next;
01859 };
01860
01861
01862
01863 if (current_pos->block == next->block) airport_flags ^= next->block;
01864
01865 Station *st = GetStation(v->u.air.targetairport);
01866 if (HASBITS(st->airport_flags, airport_flags)) {
01867 v->cur_speed = 0;
01868 v->subspeed = 0;
01869 return false;
01870 }
01871
01872 if (next->block != NOTHING_block) {
01873 SETBITS(st->airport_flags, airport_flags);
01874 }
01875 }
01876 return true;
01877 }
01878
01879 static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
01880 {
01881 Station *st = GetStation(v->u.air.targetairport);
01882 for (; i < last_terminal; i++) {
01883 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01884
01885 v->u.air.state = _airport_terminal_state[i];
01886 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01887 return true;
01888 }
01889 }
01890 return false;
01891 }
01892
01893 static uint GetNumTerminals(const AirportFTAClass *apc)
01894 {
01895 uint num = 0;
01896
01897 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01898
01899 return num;
01900 }
01901
01902 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
01903 {
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914 if (apc->terminals[0] > 1) {
01915 const Station *st = GetStation(v->u.air.targetairport);
01916 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01917
01918 while (temp != NULL) {
01919 if (temp->heading == 255) {
01920 if (!HASBITS(st->airport_flags, temp->block)) {
01921
01922
01923 uint target_group = temp->next_position + 1;
01924
01925
01926
01927
01928 uint group_start = 0;
01929 for (uint i = 1; i < target_group; i++) {
01930 group_start += apc->terminals[i];
01931 }
01932
01933 uint group_end = group_start + apc->terminals[target_group];
01934 if (FreeTerminal(v, group_start, group_end)) return true;
01935 }
01936 } else {
01937
01938
01939 return false;
01940 }
01941 temp = temp->next;
01942 }
01943 }
01944
01945
01946 return FreeTerminal(v, 0, GetNumTerminals(apc));
01947 }
01948
01949 static uint GetNumHelipads(const AirportFTAClass *apc)
01950 {
01951 uint num = 0;
01952
01953 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01954
01955 return num;
01956 }
01957
01958
01959 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
01960 {
01961
01962 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01963
01964
01965 if (apc->helipads[0] > 1) {
01966 const Station *st = GetStation(v->u.air.targetairport);
01967 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01968
01969 while (temp != NULL) {
01970 if (temp->heading == 255) {
01971 if (!HASBITS(st->airport_flags, temp->block)) {
01972
01973
01974
01975 uint target_group = temp->next_position + 1;
01976
01977
01978
01979
01980 uint group_start = 0;
01981 for (uint i = 1; i < target_group; i++) {
01982 group_start += apc->helipads[i];
01983 }
01984
01985 uint group_end = group_start + apc->helipads[target_group];
01986 if (FreeTerminal(v, group_start, group_end)) return true;
01987 }
01988 } else {
01989
01990
01991 return false;
01992 }
01993 temp = temp->next;
01994 }
01995 } else {
01996
01997
01998 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01999 }
02000 return false;
02001 }
02002
02003 static void AircraftEventHandler(Vehicle *v, int loop)
02004 {
02005 v->tick_counter++;
02006
02007 if (v->vehstatus & VS_CRASHED) {
02008 HandleCrashedAircraft(v);
02009 return;
02010 }
02011
02012 if (v->vehstatus & VS_STOPPED) return;
02013
02014
02015 if (v->breakdown_ctr != 0) {
02016 if (v->breakdown_ctr <= 2) {
02017 HandleBrokenAircraft(v);
02018 } else {
02019 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
02020 }
02021 }
02022
02023 HandleAircraftSmoke(v);
02024 ProcessOrders(v);
02025 v->HandleLoading(loop != 0);
02026
02027 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return;
02028
02029 AirportGoToNextPosition(v);
02030 }
02031
02032 void Aircraft::Tick()
02033 {
02034 if (!IsNormalAircraft(this)) return;
02035
02036 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
02037
02038 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
02039
02040 AgeAircraftCargo(this);
02041
02042 this->current_order_time++;
02043
02044 for (uint i = 0; i != 2; i++) {
02045 AircraftEventHandler(this, i);
02046 if (this->type != VEH_AIRCRAFT)
02047 break;
02048 }
02049 }
02050
02051
02057 Station *GetTargetAirportIfValid(const Vehicle *v)
02058 {
02059 assert(v->type == VEH_AIRCRAFT);
02060
02061 StationID sid = v->u.air.targetairport;
02062
02063 if (!IsValidStationID(sid)) return NULL;
02064
02065 Station *st = GetStation(sid);
02066
02067 return st->airport_tile == INVALID_TILE ? NULL : st;
02068 }
02069
02074 void UpdateAirplanesOnNewStation(const Station *st)
02075 {
02076
02077 const AirportFTAClass *ap = st->Airport();
02078
02079 Vehicle *v;
02080 FOR_ALL_VEHICLES(v) {
02081 if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
02082 if (v->u.air.targetairport == st->index) {
02083
02084
02085 if (v->u.air.state >= FLYING) {
02086 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, ap);
02087 v->u.air.state = FLYING;
02088 UpdateAircraftCache(v);
02089
02090
02091 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
02092
02093 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02094 } else {
02095 assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF);
02096 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02097
02098
02099 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02100 if (ap->layout[cnt].heading == takeofftype) {
02101 v->u.air.pos = ap->layout[cnt].position;
02102 UpdateAircraftCache(v);
02103 break;
02104 }
02105 }
02106 }
02107 }
02108 }
02109 }
02110 }