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