00001
00002
00003
00004
00005
00006
00007
00008
00009
00013 #include "stdafx.h"
00014 #include "aircraft.h"
00015 #include "debug.h"
00016 #include "landscape.h"
00017 #include "news_func.h"
00018 #include "vehicle_gui.h"
00019 #include "newgrf_engine.h"
00020 #include "newgrf_sound.h"
00021 #include "spritecache.h"
00022 #include "strings_func.h"
00023 #include "command_func.h"
00024 #include "window_func.h"
00025 #include "date_func.h"
00026 #include "vehicle_func.h"
00027 #include "sound_func.h"
00028 #include "functions.h"
00029 #include "cheat_type.h"
00030 #include "company_base.h"
00031 #include "autoreplace_gui.h"
00032 #include "ai/ai.hpp"
00033 #include "company_func.h"
00034 #include "effectvehicle_func.h"
00035 #include "station_base.h"
00036 #include "engine_base.h"
00037 #include "engine_func.h"
00038 #include "core/random_func.hpp"
00039
00040 #include "table/strings.h"
00041 #include "table/sprites.h"
00042
00043 void Aircraft::UpdateDeltaXY(Direction direction)
00044 {
00045 uint32 x;
00046 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
00047 switch (this->subtype) {
00048 default: NOT_REACHED();
00049 case AIR_AIRCRAFT:
00050 case AIR_HELICOPTER:
00051 switch (this->state) {
00052 case ENDTAKEOFF:
00053 case LANDING:
00054 case HELILANDING:
00055 case FLYING: x = MKIT(24, 24, -1, -1); break;
00056 default: x = MKIT( 2, 2, -1, -1); break;
00057 }
00058 this->z_extent = 5;
00059 break;
00060 case AIR_SHADOW: this->z_extent = 1; x = MKIT(2, 2, 0, 0); break;
00061 case AIR_ROTOR: this->z_extent = 1; x = MKIT(2, 2, -1, -1); break;
00062 }
00063 #undef MKIT
00064
00065 this->x_offs = GB(x, 0, 8);
00066 this->y_offs = GB(x, 8, 8);
00067 this->x_extent = GB(x, 16, 8);
00068 this->y_extent = GB(x, 24, 8);
00069 }
00070
00071
00074 static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22};
00075 static const byte _airport_terminal_flag[] = {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25};
00076
00077 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
00078 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00079 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00080 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
00081 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
00082 static void CrashAirplane(Aircraft *v);
00083
00084 static const SpriteID _aircraft_sprite[] = {
00085 0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
00086 0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
00087 0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
00088 0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
00089 0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
00090 0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
00091 0x0EBD, 0x0EC5
00092 };
00093
00095 enum HelicopterRotorStates {
00096 HRS_ROTOR_STOPPED,
00097 HRS_ROTOR_MOVING_1,
00098 HRS_ROTOR_MOVING_2,
00099 HRS_ROTOR_MOVING_3,
00100 };
00101
00108 static StationID FindNearestHangar(const Aircraft *v)
00109 {
00110 const Station *st;
00111 uint best = 0;
00112 StationID index = INVALID_STATION;
00113 TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
00114 const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00115
00116 FOR_ALL_STATIONS(st) {
00117 if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00118
00119 const AirportFTAClass *afc = st->Airport();
00120 const AirportSpec *as = st->GetAirportSpec();
00121 if (as->nof_depots == 0 || (
00122
00123 (afc->flags & AirportFTAClass::SHORT_STRIP) &&
00124 (avi->subtype & AIR_FAST) &&
00125 !_cheats.no_jetcrash.value)) {
00126 continue;
00127 }
00128
00129
00130 uint distance = DistanceSquare(vtile, st->airport_tile);
00131 if (distance < best || index == INVALID_STATION) {
00132 best = distance;
00133 index = st->index;
00134 }
00135 }
00136 return index;
00137 }
00138
00139 #if 0
00140
00143 static bool HaveHangarInOrderList(Aircraft *v)
00144 {
00145 const Order *order;
00146
00147 FOR_VEHICLE_ORDERS(v, order) {
00148 const Station *st = Station::Get(order->station);
00149 if (st->owner == v->owner && (st->facilities & FACIL_AIRPORT)) {
00150
00151 if (st->Airport()->nof_depots != 0)
00152 return true;
00153 }
00154 }
00155
00156 return false;
00157 }
00158 #endif
00159
00160 SpriteID Aircraft::GetImage(Direction direction) const
00161 {
00162 uint8 spritenum = this->spritenum;
00163
00164 if (is_custom_sprite(spritenum)) {
00165 SpriteID sprite = GetCustomVehicleSprite(this, direction);
00166 if (sprite != 0) return sprite;
00167
00168 spritenum = Engine::Get(this->engine_type)->original_image_index;
00169 }
00170
00171 return direction + _aircraft_sprite[spritenum];
00172 }
00173
00174 SpriteID GetRotorImage(const Aircraft *v)
00175 {
00176 assert(v->subtype == AIR_HELICOPTER);
00177
00178 const Aircraft *w = v->Next()->Next();
00179 if (is_custom_sprite(v->spritenum)) {
00180 SpriteID sprite = GetCustomRotorSprite(v, false);
00181 if (sprite != 0) return sprite;
00182 }
00183
00184
00185 return SPR_ROTOR_STOPPED + w->state;
00186 }
00187
00188 static SpriteID GetAircraftIcon(EngineID engine)
00189 {
00190 const Engine *e = Engine::Get(engine);
00191 uint8 spritenum = e->u.air.image_index;
00192
00193 if (is_custom_sprite(spritenum)) {
00194 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00195 if (sprite != 0) return sprite;
00196
00197 spritenum = e->original_image_index;
00198 }
00199
00200 return DIR_W + _aircraft_sprite[spritenum];
00201 }
00202
00203 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal)
00204 {
00205 SpriteID sprite = GetAircraftIcon(engine);
00206 const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00207 preferred_x = Clamp(preferred_x, left - real_sprite->x_offs, right - real_sprite->width - real_sprite->x_offs);
00208 DrawSprite(sprite, pal, preferred_x, y);
00209
00210 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00211 SpriteID rotor_sprite = GetCustomRotorIcon(engine);
00212 if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00213 DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00214 }
00215 }
00216
00222 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
00223 {
00224 const Sprite *spr = GetSprite(GetAircraftIcon(engine), ST_NORMAL);
00225
00226 width = spr->width;
00227 height = spr->height;
00228 }
00229
00238 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00239 {
00240 if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_ERROR_AIRCRAFT_NOT_AVAILABLE);
00241
00242 const Engine *e = Engine::Get(p1);
00243 const AircraftVehicleInfo *avi = &e->u.air;
00244 CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
00245
00246
00247 if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00248
00249
00250 if (flags & DC_QUERY_COST) return value;
00251
00252 if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00253
00254
00255 if (!CanVehicleUseStation(p1, Station::GetByTile(tile))) return CMD_ERROR;
00256
00257
00258 if (!Vehicle::CanAllocateItem(avi->subtype & AIR_CTOL ? 2 : 3)) {
00259 return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
00260 }
00261
00262 UnitID unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
00263 if (unit_num > _settings_game.vehicle.max_aircraft)
00264 return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
00265
00266 if (flags & DC_EXEC) {
00267 Aircraft *v = new Aircraft();
00268 Aircraft *u = new Aircraft();
00269
00270 v->unitnumber = unit_num;
00271 v->direction = DIR_SE;
00272
00273 v->owner = u->owner = _current_company;
00274
00275 v->tile = tile;
00276
00277 uint x = TileX(tile) * TILE_SIZE + 5;
00278 uint y = TileY(tile) * TILE_SIZE + 3;
00279
00280 v->x_pos = u->x_pos = x;
00281 v->y_pos = u->y_pos = y;
00282
00283 u->z_pos = GetSlopeZ(x, y);
00284 v->z_pos = u->z_pos + 1;
00285
00286 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00287 u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00288
00289 v->spritenum = avi->image_index;
00290
00291 v->cargo_cap = avi->passenger_capacity;
00292 u->cargo_cap = avi->mail_capacity;
00293
00294 v->cargo_type = e->GetDefaultCargoType();
00295 u->cargo_type = CT_MAIL;
00296
00297 v->name = NULL;
00298 v->last_station_visited = INVALID_STATION;
00299
00300 v->max_speed = avi->max_speed;
00301 v->acceleration = avi->acceleration;
00302 v->engine_type = p1;
00303 u->engine_type = p1;
00304
00305 v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00306 v->UpdateDeltaXY(INVALID_DIR);
00307 v->value = value.GetCost();
00308
00309 u->subtype = AIR_SHADOW;
00310 u->UpdateDeltaXY(INVALID_DIR);
00311
00312 v->reliability = e->reliability;
00313 v->reliability_spd_dec = e->reliability_spd_dec;
00314 v->max_age = e->GetLifeLengthInDays();
00315
00316 _new_vehicle_id = v->index;
00317
00318
00319
00320
00321
00322 for (uint i = 0;; i++) {
00323 const Station *st = Station::GetByTile(tile);
00324 const AirportFTAClass *apc = st->Airport();
00325
00326 if (st->GetHangarTile(i) == tile) {
00327 assert(apc->layout[i].heading == HANGAR);
00328 v->pos = apc->layout[i].position;
00329 break;
00330 }
00331 }
00332
00333 v->state = HANGAR;
00334 v->previous_pos = v->pos;
00335 v->targetairport = GetStationIndex(tile);
00336 v->SetNext(u);
00337
00338 v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_aircraft;
00339
00340 v->date_of_last_service = _date;
00341 v->build_year = u->build_year = _cur_year;
00342
00343 v->cur_image = u->cur_image = SPR_IMG_QUERY;
00344
00345 v->random_bits = VehicleRandomBits();
00346 u->random_bits = VehicleRandomBits();
00347
00348 v->vehicle_flags = 0;
00349 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00350
00351 v->InvalidateNewGRFCacheOfChain();
00352
00353 v->cargo_cap = GetVehicleCapacity(v, &u->cargo_cap);
00354
00355 v->InvalidateNewGRFCacheOfChain();
00356
00357 UpdateAircraftCache(v);
00358
00359 VehicleMove(v, false);
00360 VehicleMove(u, false);
00361
00362
00363 if (v->subtype == AIR_HELICOPTER) {
00364 Aircraft *w = new Aircraft();
00365 w->engine_type = p1;
00366 w->direction = DIR_N;
00367 w->owner = _current_company;
00368 w->x_pos = v->x_pos;
00369 w->y_pos = v->y_pos;
00370 w->z_pos = v->z_pos + 5;
00371 w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00372 w->spritenum = 0xFF;
00373 w->subtype = AIR_ROTOR;
00374 w->cur_image = SPR_ROTOR_STOPPED;
00375 w->random_bits = VehicleRandomBits();
00376
00377 w->state = HRS_ROTOR_STOPPED;
00378 w->UpdateDeltaXY(INVALID_DIR);
00379
00380 u->SetNext(w);
00381 VehicleMove(w, false);
00382 }
00383
00384 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00385 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00386 SetWindowDirty(WC_COMPANY, v->owner);
00387 if (IsLocalCompany())
00388 InvalidateAutoreplaceWindow(v->engine_type, v->group_id);
00389
00390 Company::Get(_current_company)->num_engines[p1]++;
00391 }
00392
00393 return value;
00394 }
00395
00396
00405 CommandCost CmdSellAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00406 {
00407 Aircraft *v = Aircraft::GetIfValid(p1);
00408
00409 if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
00410 if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_AIRCRAFT_MUST_BE_STOPPED);
00411
00412 if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_SELL_DESTROYED_VEHICLE);
00413
00414 CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00415
00416 if (flags & DC_EXEC) {
00417 delete v;
00418 }
00419
00420 return ret;
00421 }
00422
00423 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00424 {
00425 const Station *st = GetTargetAirportIfValid(this);
00426
00427 if (st == NULL || st->GetAirportSpec()->nof_depots == 0) {
00428
00429 StationID station = FindNearestHangar(this);
00430
00431 if (station == INVALID_STATION) return false;
00432
00433 st = Station::Get(station);
00434 }
00435
00436 if (location != NULL) *location = st->xy;
00437 if (destination != NULL) *destination = st->index;
00438
00439 return true;
00440 }
00441
00452 CommandCost CmdSendAircraftToHangar(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00453 {
00454 if (p2 & DEPOT_MASS_SEND) {
00455
00456 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00457 return SendAllVehiclesToDepot(VEH_AIRCRAFT, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00458 }
00459
00460 Aircraft *v = Aircraft::GetIfValid(p1);
00461 if (v == NULL) return CMD_ERROR;
00462
00463 return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00464 }
00465
00466
00478 CommandCost CmdRefitAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00479 {
00480 byte new_subtype = GB(p2, 8, 8);
00481
00482 Aircraft *v = Aircraft::GetIfValid(p1);
00483 if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
00484 if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_AIRCRAFT_MUST_BE_STOPPED);
00485 if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_REFIT_DESTROYED_VEHICLE);
00486
00487
00488 CargoID new_cid = GB(p2, 0, 8);
00489 if (new_cid >= NUM_CARGO) return CMD_ERROR;
00490
00491 CommandCost cost = RefitVehicle(v, true, new_cid, new_subtype, flags);
00492
00493 if (flags & DC_EXEC) {
00494 v->colourmap = PAL_NONE;
00495 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
00496 SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
00497 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00498 }
00499 v->InvalidateNewGRFCacheOfChain();
00500
00501 return cost;
00502 }
00503
00504
00505 static void CheckIfAircraftNeedsService(Aircraft *v)
00506 {
00507 if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00508 if (v->IsInDepot()) {
00509 VehicleServiceInDepot(v);
00510 return;
00511 }
00512
00513
00514
00515 if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
00516
00517 const Station *st = Station::Get(v->current_order.GetDestination());
00518
00519 assert(st != NULL);
00520
00521
00522 if (st->GetAirportSpec()->nof_depots > 0 && CanVehicleUseStation(v, st)) {
00523 v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00524 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00525 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00526 v->current_order.MakeDummy();
00527 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00528 }
00529 }
00530
00531 Money Aircraft::GetRunningCost() const
00532 {
00533 const Engine *e = Engine::Get(this->engine_type);
00534 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00535 return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->grffile);
00536 }
00537
00538 void Aircraft::OnNewDay()
00539 {
00540 if (!this->IsNormalAircraft()) return;
00541
00542 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00543
00544 CheckOrders(this);
00545
00546 CheckVehicleBreakdown(this);
00547 AgeVehicle(this);
00548 CheckIfAircraftNeedsService(this);
00549
00550 if (this->running_ticks == 0) return;
00551
00552 CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00553
00554 this->profit_this_year -= cost.GetCost();
00555 this->running_ticks = 0;
00556
00557 SubtractMoneyFromCompanyFract(this->owner, cost);
00558
00559 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00560 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00561 }
00562
00563 static void HelicopterTickHandler(Aircraft *v)
00564 {
00565 Aircraft *u = v->Next()->Next();
00566
00567 if (u->vehstatus & VS_HIDDEN) return;
00568
00569
00570
00571 if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00572 if (u->cur_speed != 0) {
00573 u->cur_speed++;
00574 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00575 u->cur_speed = 0;
00576 }
00577 }
00578 } else {
00579 if (u->cur_speed == 0)
00580 u->cur_speed = 0x70;
00581
00582 if (u->cur_speed >= 0x50)
00583 u->cur_speed--;
00584 }
00585
00586 int tick = ++u->tick_counter;
00587 int spd = u->cur_speed >> 4;
00588
00589 SpriteID img;
00590 if (spd == 0) {
00591 u->state = HRS_ROTOR_STOPPED;
00592 img = GetRotorImage(v);
00593 if (u->cur_image == img) return;
00594 } else if (tick >= spd) {
00595 u->tick_counter = 0;
00596 u->state++;
00597 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00598 img = GetRotorImage(v);
00599 } else {
00600 return;
00601 }
00602
00603 u->cur_image = img;
00604
00605 VehicleMove(u, true);
00606 }
00607
00608 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00609 {
00610 v->x_pos = x;
00611 v->y_pos = y;
00612 v->z_pos = z;
00613
00614 v->UpdateViewport(true, false);
00615 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00616
00617 Aircraft *u = v->Next();
00618
00619 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00620 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00621 u->x_pos = x;
00622 u->y_pos = y - ((v->z_pos - GetSlopeZ(safe_x, safe_y)) >> 3);
00623
00624 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00625 u->z_pos = GetSlopeZ(safe_x, safe_y);
00626 u->cur_image = v->cur_image;
00627
00628 VehicleMove(u, true);
00629
00630 u = u->Next();
00631 if (u != NULL) {
00632 u->x_pos = x;
00633 u->y_pos = y;
00634 u->z_pos = z + 5;
00635
00636 VehicleMove(u, true);
00637 }
00638 }
00639
00643 void HandleAircraftEnterHangar(Aircraft *v)
00644 {
00645 v->subspeed = 0;
00646 v->progress = 0;
00647
00648 Aircraft *u = v->Next();
00649 u->vehstatus |= VS_HIDDEN;
00650 u = u->Next();
00651 if (u != NULL) {
00652 u->vehstatus |= VS_HIDDEN;
00653 u->cur_speed = 0;
00654 }
00655
00656 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00657 }
00658
00659 static void PlayAircraftSound(const Vehicle *v)
00660 {
00661 if (!PlayVehicleSound(v, VSE_START)) {
00662 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00663 }
00664 }
00665
00666
00667 void UpdateAircraftCache(Aircraft *v)
00668 {
00669 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00670 if (max_speed != 0) {
00671
00672 max_speed = (max_speed * 129) / 10;
00673
00674 v->acache.cached_max_speed = max_speed;
00675 } else {
00676 v->acache.cached_max_speed = 0xFFFF;
00677 }
00678 }
00679
00680
00684 enum AircraftSpeedLimits {
00685 SPEED_LIMIT_TAXI = 50,
00686 SPEED_LIMIT_APPROACH = 230,
00687 SPEED_LIMIT_BROKEN = 320,
00688 SPEED_LIMIT_HOLD = 425,
00689 SPEED_LIMIT_NONE = 0xFFFF
00690 };
00691
00699 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00700 {
00701 uint spd = v->acceleration * 16;
00702 byte t;
00703
00704
00705
00706 speed_limit *= _settings_game.vehicle.plane_speed;
00707
00708 if (v->acache.cached_max_speed < speed_limit) {
00709 if (v->cur_speed < speed_limit) hard_limit = false;
00710 speed_limit = v->acache.cached_max_speed;
00711 }
00712
00713 speed_limit = min(speed_limit, v->max_speed);
00714
00715 v->subspeed = (t = v->subspeed) + (byte)spd;
00716
00717
00718
00719
00720
00721
00722
00723 if (!hard_limit && v->cur_speed > speed_limit) {
00724 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00725 }
00726
00727 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00728
00729
00730 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00731
00732
00733 if (spd != v->cur_speed) {
00734 v->cur_speed = spd;
00735 if (_settings_client.gui.vehicle_speed)
00736 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00737 }
00738
00739
00740 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00741
00742 if (!(v->direction & 1)) spd = spd * 3 / 4;
00743
00744 spd += v->progress;
00745 v->progress = (byte)spd;
00746 return spd >> 8;
00747 }
00748
00756 byte GetAircraftFlyingAltitude(const Aircraft *v)
00757 {
00758
00759
00760
00761 byte base_altitude = 150;
00762
00763
00764
00765
00766 switch (v->direction) {
00767 case DIR_N:
00768 case DIR_NE:
00769 case DIR_E:
00770 case DIR_SE:
00771 base_altitude += 10;
00772 break;
00773
00774 default: break;
00775 }
00776
00777
00778 base_altitude += min(20 * (v->max_speed / 200), 90);
00779
00780 return base_altitude;
00781 }
00782
00796 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc)
00797 {
00798 assert(v != NULL);
00799 assert(apc != NULL);
00800
00801
00802
00803
00804 TileIndex tile = 0;
00805
00806 const Station *st = Station::GetIfValid(v->targetairport);
00807 if (st != NULL) {
00808
00809 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00810 }
00811
00812 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00813 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00814
00815 DiagDirection dir;
00816 if (abs(delta_y) < abs(delta_x)) {
00817
00818 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00819 } else {
00820
00821 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00822 }
00823 return apc->entry_points[dir];
00824 }
00825
00826
00827 static void MaybeCrashAirplane(Aircraft *v);
00828
00836 static bool AircraftController(Aircraft *v)
00837 {
00838 int count;
00839
00840
00841 const Station *st = Station::GetIfValid(v->targetairport);
00842
00843 TileIndex tile = INVALID_TILE;
00844 if (st != NULL) {
00845 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00846 }
00847
00848 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00849
00850
00851 if (st == NULL || st->airport_tile == INVALID_TILE) {
00852
00853 if (v->pos >= afc->nofelements) {
00854 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc);
00855 } else if (v->targetairport != v->current_order.GetDestination()) {
00856
00857 v->state = FLYING;
00858 UpdateAircraftCache(v);
00859 AircraftNextAirportPos_and_Order(v);
00860
00861 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00862 return false;
00863 }
00864 }
00865
00866
00867 const AirportMovingData *amd = afc->MovingData(v->pos);
00868
00869 int x = TileX(tile) * TILE_SIZE;
00870 int y = TileY(tile) * TILE_SIZE;
00871
00872
00873 if (amd->flag & AMED_HELI_RAISE) {
00874 Aircraft *u = v->Next()->Next();
00875
00876
00877 if (u->cur_speed > 32) {
00878 v->cur_speed = 0;
00879 if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00880 } else {
00881 u->cur_speed = 32;
00882 count = UpdateAircraftSpeed(v);
00883 if (count > 0) {
00884 v->tile = 0;
00885
00886
00887 if (v->z_pos >= 184) {
00888 v->cur_speed = 0;
00889 return true;
00890 }
00891 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00892 }
00893 }
00894 return false;
00895 }
00896
00897
00898 if (amd->flag & AMED_HELI_LOWER) {
00899 if (st == NULL) {
00900
00901
00902
00903 v->state = FLYING;
00904 UpdateAircraftCache(v);
00905 AircraftNextAirportPos_and_Order(v);
00906 return false;
00907 }
00908
00909
00910 v->tile = tile;
00911
00912
00913 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00914
00915 if (z == v->z_pos) {
00916 Vehicle *u = v->Next()->Next();
00917
00918
00919 if (u->cur_speed >= 80) return true;
00920 u->cur_speed += 4;
00921 } else {
00922 count = UpdateAircraftSpeed(v);
00923 if (count > 0) {
00924 if (v->z_pos > z) {
00925 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00926 } else {
00927 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00928 }
00929 }
00930 }
00931 return false;
00932 }
00933
00934
00935 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
00936
00937
00938 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00939
00940
00941 if (dist == 0) {
00942
00943 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
00944
00945
00946 if (dirdiff == DIRDIFF_SAME) {
00947 v->cur_speed = 0;
00948 return true;
00949 }
00950
00951 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00952
00953 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00954 v->cur_speed >>= 1;
00955
00956 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00957 return false;
00958 }
00959
00960 if (amd->flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00961 MaybeCrashAirplane(v);
00962 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00963 }
00964
00965 uint speed_limit = SPEED_LIMIT_TAXI;
00966 bool hard_limit = true;
00967
00968 if (amd->flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00969 if (amd->flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00970 if (amd->flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00971 if (amd->flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00972
00973 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00974 if (count == 0) return false;
00975
00976 if (v->turn_counter != 0) v->turn_counter--;
00977
00978 do {
00979
00980 GetNewVehiclePosResult gp;
00981
00982 if (dist < 4 || (amd->flag & AMED_LAND)) {
00983
00984 gp.x = (v->x_pos != (x + amd->x)) ?
00985 v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
00986 v->x_pos;
00987 gp.y = (v->y_pos != (y + amd->y)) ?
00988 v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
00989 v->y_pos;
00990
00991
00992 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
00993
00994 } else {
00995
00996
00997 Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
00998 if (newdir != v->direction) {
00999 if (amd->flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
01000 if (v->turn_counter == 0 || newdir == v->last_direction) {
01001 if (newdir == v->last_direction) {
01002 v->number_consecutive_turns = 0;
01003 } else {
01004 v->number_consecutive_turns++;
01005 }
01006 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
01007 v->last_direction = v->direction;
01008 v->direction = newdir;
01009 }
01010
01011
01012 gp = GetNewVehiclePos(v);
01013 } else {
01014 v->cur_speed >>= 1;
01015 v->direction = newdir;
01016
01017
01018
01019
01020
01021
01022 gp.x = v->x_pos;
01023 gp.y = v->y_pos;
01024 gp.new_tile = gp.old_tile = v->tile;
01025 }
01026 } else {
01027 v->number_consecutive_turns = 0;
01028
01029 gp = GetNewVehiclePos(v);
01030 }
01031 }
01032
01033 v->tile = gp.new_tile;
01034
01035 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01036
01037
01038 uint z = v->z_pos;
01039
01040 if (amd->flag & AMED_TAKEOFF) {
01041 z = min(z + 2, GetAircraftFlyingAltitude(v));
01042 }
01043
01044 if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01045
01046 if (amd->flag & AMED_LAND) {
01047 if (st->airport_tile == INVALID_TILE) {
01048
01049 v->state = FLYING;
01050 UpdateAircraftCache(v);
01051 AircraftNextAirportPos_and_Order(v);
01052
01053 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01054 continue;
01055 }
01056
01057 uint curz = GetSlopeZ(x + amd->x, y + amd->y) + 1;
01058
01059
01060 assert(curz <= z);
01061 int t = max(1U, dist - 4);
01062 int delta = z - curz;
01063
01064
01065 if (delta >= t) {
01066 z -= ((z - curz) + t - 1) / t;
01067 }
01068 if (z < curz) z = curz;
01069 }
01070
01071
01072 if (amd->flag & AMED_BRAKE) {
01073 uint curz = GetSlopeZ(x, y) + 1;
01074
01075 if (z > curz) {
01076 z--;
01077 } else if (z < curz) {
01078 z++;
01079 }
01080
01081 }
01082
01083 SetAircraftPosition(v, gp.x, gp.y, z);
01084 } while (--count != 0);
01085 return false;
01086 }
01087
01088
01089 static bool HandleCrashedAircraft(Aircraft *v)
01090 {
01091 v->crashed_counter += 3;
01092
01093 Station *st = GetTargetAirportIfValid(v);
01094
01095
01096 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01097 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01098 v->z_pos -= 1;
01099 if (v->z_pos == z) {
01100 v->crashed_counter = 500;
01101 v->z_pos++;
01102 }
01103 }
01104
01105 if (v->crashed_counter < 650) {
01106 uint32 r;
01107 if (Chance16R(1, 32, r)) {
01108 static const DirDiff delta[] = {
01109 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01110 };
01111
01112 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01113 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01114 r = Random();
01115 CreateEffectVehicleRel(v,
01116 GB(r, 0, 4) - 4,
01117 GB(r, 4, 4) - 4,
01118 GB(r, 8, 4),
01119 EV_EXPLOSION_SMALL);
01120 }
01121 } else if (v->crashed_counter >= 10000) {
01122
01123
01124
01125
01126
01127 if (st != NULL) {
01128 CLRBITS(st->airport_flags, RUNWAY_IN_block);
01129 CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block);
01130 CLRBITS(st->airport_flags, RUNWAY_IN2_block);
01131 }
01132
01133 delete v;
01134
01135 return false;
01136 }
01137
01138 return true;
01139 }
01140
01141 static void HandleBrokenAircraft(Aircraft *v)
01142 {
01143 if (v->breakdown_ctr != 1) {
01144 v->breakdown_ctr = 1;
01145 v->vehstatus |= VS_AIRCRAFT_BROKEN;
01146
01147 if (v->breakdowns_since_last_service != 255)
01148 v->breakdowns_since_last_service++;
01149 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01150 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01151 }
01152 }
01153
01154
01155 static void HandleAircraftSmoke(Aircraft *v)
01156 {
01157 static const struct {
01158 int8 x;
01159 int8 y;
01160 } smoke_pos[] = {
01161 { 5, 5 },
01162 { 6, 0 },
01163 { 5, -5 },
01164 { 0, -6 },
01165 { -5, -5 },
01166 { -6, 0 },
01167 { -5, 5 },
01168 { 0, 6 }
01169 };
01170
01171 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01172
01173 if (v->cur_speed < 10) {
01174 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01175 v->breakdown_ctr = 0;
01176 return;
01177 }
01178
01179 if ((v->tick_counter & 0x1F) == 0) {
01180 CreateEffectVehicleRel(v,
01181 smoke_pos[v->direction].x,
01182 smoke_pos[v->direction].y,
01183 2,
01184 EV_SMOKE
01185 );
01186 }
01187 }
01188
01189 void HandleMissingAircraftOrders(Aircraft *v)
01190 {
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206 const Station *st = GetTargetAirportIfValid(v);
01207 if (st == NULL) {
01208 CommandCost ret;
01209 CompanyID old_company = _current_company;
01210
01211 _current_company = v->owner;
01212 ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01213 _current_company = old_company;
01214
01215 if (ret.Failed()) CrashAirplane(v);
01216 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01217 v->current_order.Free();
01218 }
01219 }
01220
01221
01222 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01223 {
01224
01225 if (this->state == FLYING) {
01226 AircraftNextAirportPos_and_Order(this);
01227 }
01228
01229
01230 return 0;
01231 }
01232
01233 void Aircraft::MarkDirty()
01234 {
01235 this->UpdateViewport(false, false);
01236 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01237 }
01238
01239
01240 uint Aircraft::Crash(bool flooded)
01241 {
01242 uint pass = Vehicle::Crash(flooded) + 2;
01243 this->crashed_counter = flooded ? 9000 : 0;
01244
01245 return pass;
01246 }
01247
01248 static void CrashAirplane(Aircraft *v)
01249 {
01250 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01251
01252 uint pass = v->Crash();
01253 SetDParam(0, pass);
01254
01255 v->cargo.Truncate(0);
01256 v->Next()->cargo.Truncate(0);
01257 const Station *st = GetTargetAirportIfValid(v);
01258 StringID newsitem;
01259 if (st == NULL) {
01260 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01261 } else {
01262 SetDParam(1, st->index);
01263 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01264 }
01265
01266 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, st == NULL ? AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : AIEventVehicleCrashed::CRASH_PLANE_LANDING));
01267
01268 AddVehicleNewsItem(newsitem,
01269 NS_ACCIDENT,
01270 v->index,
01271 st != NULL ? st->index : INVALID_STATION);
01272
01273 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01274 }
01275
01276 static void MaybeCrashAirplane(Aircraft *v)
01277 {
01278 if (_settings_game.vehicle.plane_crashes == 0) return;
01279
01280 Station *st = Station::Get(v->targetairport);
01281
01282
01283 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01284 if ((st->Airport()->flags & AirportFTAClass::SHORT_STRIP) &&
01285 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01286 !_cheats.no_jetcrash.value) {
01287 prob /= 20;
01288 } else {
01289 prob /= 1500;
01290 }
01291
01292 if (GB(Random(), 0, 22) > prob) return;
01293
01294
01295 for (CargoID i = 0; i < NUM_CARGO; i++) {
01296 st->goods[i].rating = 1;
01297 st->goods[i].cargo.Truncate(0);
01298 }
01299
01300 CrashAirplane(v);
01301 }
01302
01304 static void AircraftEntersTerminal(Aircraft *v)
01305 {
01306 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01307
01308 Station *st = Station::Get(v->targetairport);
01309 v->last_station_visited = v->targetairport;
01310
01311
01312 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01313 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01314 SetDParam(0, st->index);
01315
01316 AddVehicleNewsItem(
01317 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01318 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01319 v->index,
01320 st->index
01321 );
01322 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01323 }
01324
01325 v->BeginLoading();
01326 }
01327
01328 static void AircraftLandAirplane(Aircraft *v)
01329 {
01330 v->UpdateDeltaXY(INVALID_DIR);
01331
01332 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01333 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01334 }
01335 }
01336
01337
01339 void AircraftNextAirportPos_and_Order(Aircraft *v)
01340 {
01341 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01342 v->targetairport = v->current_order.GetDestination();
01343 }
01344
01345 const Station *st = GetTargetAirportIfValid(v);
01346 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01347 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc);
01348 }
01349
01350 void AircraftLeaveHangar(Aircraft *v)
01351 {
01352 v->cur_speed = 0;
01353 v->subspeed = 0;
01354 v->progress = 0;
01355 v->direction = DIR_SE;
01356 v->vehstatus &= ~VS_HIDDEN;
01357 {
01358 Vehicle *u = v->Next();
01359 u->vehstatus &= ~VS_HIDDEN;
01360
01361
01362 u = u->Next();
01363 if (u != NULL) {
01364 u->vehstatus &= ~VS_HIDDEN;
01365 u->cur_speed = 80;
01366 }
01367 }
01368
01369 VehicleServiceInDepot(v);
01370 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01371 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01372 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01373 }
01374
01378 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01379 {
01380 AircraftEntersTerminal(v);
01381 v->state = apc->layout[v->pos].heading;
01382 }
01383
01384 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01385 {
01386 VehicleEnterDepot(v);
01387 v->state = apc->layout[v->pos].heading;
01388 }
01389
01391 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01392 {
01393
01394 if (v->previous_pos != v->pos) {
01395 AircraftEventHandler_EnterHangar(v, apc);
01396 return;
01397 }
01398
01399
01400 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01401 v->current_order.Free();
01402 return;
01403 }
01404
01405 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01406 !v->current_order.IsType(OT_GOTO_DEPOT))
01407 return;
01408
01409
01410 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01411
01412
01413 if (v->current_order.GetDestination() == v->targetairport) {
01414
01415
01416 if (v->subtype == AIR_HELICOPTER) {
01417 if (!AirportFindFreeHelipad(v, apc)) return;
01418 } else {
01419 if (!AirportFindFreeTerminal(v, apc)) return;
01420 }
01421 } else {
01422
01423 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01424 }
01425 AircraftLeaveHangar(v);
01426 AirportMove(v, apc);
01427 }
01428
01430 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01431 {
01432
01433 if (v->previous_pos != v->pos) {
01434 AircraftEventHandler_EnterTerminal(v, apc);
01435
01436
01437 if (_settings_game.order.serviceathelipad) {
01438 if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01439
01440 v->date_of_last_service = _date;
01441 v->breakdowns_since_last_service = 0;
01442 v->reliability = Engine::Get(v->engine_type)->reliability;
01443 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01444 }
01445 }
01446 return;
01447 }
01448
01449 if (v->current_order.IsType(OT_NOTHING)) return;
01450
01451
01452 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01453
01454
01455
01456
01457 bool go_to_hangar = false;
01458 switch (v->current_order.GetType()) {
01459 case OT_GOTO_STATION:
01460 break;
01461 case OT_GOTO_DEPOT:
01462 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01463 break;
01464 case OT_CONDITIONAL:
01465
01466
01467
01468 return;
01469 default:
01470 v->current_order.Free();
01471 go_to_hangar = Station::Get(v->targetairport)->GetAirportSpec()->nof_depots != 0;
01472 }
01473
01474 if (go_to_hangar) {
01475 v->state = HANGAR;
01476 } else {
01477
01478 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01479 }
01480 AirportMove(v, apc);
01481 }
01482
01483 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01484 {
01485 error("OK, you shouldn't be here, check your Airport Scheme!");
01486 }
01487
01488 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01489 {
01490 PlayAircraftSound(v);
01491 v->state = STARTTAKEOFF;
01492 }
01493
01494 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01495 {
01496 v->state = ENDTAKEOFF;
01497 v->UpdateDeltaXY(INVALID_DIR);
01498 }
01499
01500 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01501 {
01502 v->state = FLYING;
01503
01504 AircraftNextAirportPos_and_Order(v);
01505 }
01506
01507 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01508 {
01509 v->state = FLYING;
01510 v->UpdateDeltaXY(INVALID_DIR);
01511
01512
01513 AircraftNextAirportPos_and_Order(v);
01514
01515
01516 if (v->NeedsAutomaticServicing()) {
01517 _current_company = v->owner;
01518 DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01519 _current_company = OWNER_NONE;
01520 }
01521 }
01522
01523 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01524 {
01525 Station *st = Station::Get(v->targetairport);
01526
01527
01528 if ((apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES)) &&
01529 st->airport_tile != INVALID_TILE &&
01530 (st->owner == OWNER_NONE || st->owner == v->owner)) {
01531
01532
01533
01534 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01535 const AirportFTA *current = apc->layout[v->pos].next;
01536 while (current != NULL) {
01537 if (current->heading == landingtype) {
01538
01539
01540
01541 uint16 tcur_speed = v->cur_speed;
01542 uint16 tsubspeed = v->subspeed;
01543 if (!AirportHasBlock(v, current, apc)) {
01544 v->state = landingtype;
01545
01546
01547
01548 v->pos = current->next_position;
01549 SETBITS(st->airport_flags, apc->layout[v->pos].block);
01550 return;
01551 }
01552 v->cur_speed = tcur_speed;
01553 v->subspeed = tsubspeed;
01554 }
01555 current = current->next;
01556 }
01557 }
01558 v->state = FLYING;
01559 v->pos = apc->layout[v->pos].next_position;
01560 }
01561
01562 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01563 {
01564 v->state = ENDLANDING;
01565 AircraftLandAirplane(v);
01566
01567
01568 if (v->NeedsAutomaticServicing()) {
01569 _current_company = v->owner;
01570 DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01571 _current_company = OWNER_NONE;
01572 }
01573 }
01574
01575 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01576 {
01577 v->state = HELIENDLANDING;
01578 v->UpdateDeltaXY(INVALID_DIR);
01579 }
01580
01581 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01582 {
01583
01584 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01585
01586
01587
01588
01589
01590 if (v->current_order.IsType(OT_GOTO_STATION)) {
01591 if (AirportFindFreeTerminal(v, apc)) return;
01592 }
01593 v->state = HANGAR;
01594
01595 }
01596
01597 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01598 {
01599
01600 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01601
01602
01603
01604
01605
01606
01607
01608
01609 if (v->current_order.IsType(OT_GOTO_STATION)) {
01610 if (AirportFindFreeHelipad(v, apc)) return;
01611 }
01612 const AirportSpec *as = Station::Get(v->targetairport)->GetAirportSpec();
01613 v->state = (as->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01614 }
01615
01616 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01617 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01618 AircraftEventHandler_General,
01619 AircraftEventHandler_InHangar,
01620 AircraftEventHandler_AtTerminal,
01621 AircraftEventHandler_AtTerminal,
01622 AircraftEventHandler_AtTerminal,
01623 AircraftEventHandler_AtTerminal,
01624 AircraftEventHandler_AtTerminal,
01625 AircraftEventHandler_AtTerminal,
01626 AircraftEventHandler_AtTerminal,
01627 AircraftEventHandler_AtTerminal,
01628 AircraftEventHandler_TakeOff,
01629 AircraftEventHandler_StartTakeOff,
01630 AircraftEventHandler_EndTakeOff,
01631 AircraftEventHandler_HeliTakeOff,
01632 AircraftEventHandler_Flying,
01633 AircraftEventHandler_Landing,
01634 AircraftEventHandler_EndLanding,
01635 AircraftEventHandler_HeliLanding,
01636 AircraftEventHandler_HeliEndLanding,
01637 AircraftEventHandler_AtTerminal,
01638 AircraftEventHandler_AtTerminal,
01639 AircraftEventHandler_AtTerminal,
01640 AircraftEventHandler_AtTerminal,
01641 };
01642
01643 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01644 {
01645
01646 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01647 Station *st = Station::Get(v->targetairport);
01648
01649 CLRBITS(st->airport_flags, apc->layout[v->previous_pos].block);
01650 }
01651 }
01652
01653 static void AirportGoToNextPosition(Aircraft *v)
01654 {
01655
01656 if (!AircraftController(v)) return;
01657
01658 const AirportFTAClass *apc = Station::Get(v->targetairport)->Airport();
01659
01660 AirportClearBlock(v, apc);
01661 AirportMove(v, apc);
01662 }
01663
01664
01665 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01666 {
01667
01668 if (v->pos >= apc->nofelements) {
01669 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01670 assert(v->pos < apc->nofelements);
01671 }
01672
01673 const AirportFTA *current = &apc->layout[v->pos];
01674
01675 if (current->heading == v->state) {
01676 byte prev_pos = v->pos;
01677 byte prev_state = v->state;
01678 _aircraft_state_handlers[v->state](v, apc);
01679 if (v->state != FLYING) v->previous_pos = prev_pos;
01680 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01681 return true;
01682 }
01683
01684 v->previous_pos = v->pos;
01685
01686
01687 if (current->next == NULL) {
01688 if (AirportSetBlocks(v, current, apc)) {
01689 v->pos = current->next_position;
01690 UpdateAircraftCache(v);
01691 }
01692 return false;
01693 }
01694
01695
01696
01697 do {
01698 if (v->state == current->heading || current->heading == TO_ALL) {
01699 if (AirportSetBlocks(v, current, apc)) {
01700 v->pos = current->next_position;
01701 UpdateAircraftCache(v);
01702 }
01703 return false;
01704 }
01705 current = current->next;
01706 } while (current != NULL);
01707
01708 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01709 NOT_REACHED();
01710 }
01711
01712
01713 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01714 {
01715 const AirportFTA *reference = &apc->layout[v->pos];
01716 const AirportFTA *next = &apc->layout[current_pos->next_position];
01717
01718
01719 if (apc->layout[current_pos->position].block != next->block) {
01720 const Station *st = Station::Get(v->targetairport);
01721 uint64 airport_flags = next->block;
01722
01723
01724 if (current_pos != reference && current_pos->block != NOTHING_block) {
01725 airport_flags |= current_pos->block;
01726 }
01727
01728 if (st->airport_flags & airport_flags) {
01729 v->cur_speed = 0;
01730 v->subspeed = 0;
01731 return true;
01732 }
01733 }
01734 return false;
01735 }
01736
01744 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01745 {
01746 const AirportFTA *next = &apc->layout[current_pos->next_position];
01747 const AirportFTA *reference = &apc->layout[v->pos];
01748
01749
01750 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01751 uint64 airport_flags = next->block;
01752
01753
01754 const AirportFTA *current = current_pos;
01755 if (current == reference) current = current->next;
01756 while (current != NULL) {
01757 if (current->heading == current_pos->heading && current->block != 0) {
01758 airport_flags |= current->block;
01759 break;
01760 }
01761 current = current->next;
01762 };
01763
01764
01765
01766 if (current_pos->block == next->block) airport_flags ^= next->block;
01767
01768 Station *st = Station::Get(v->targetairport);
01769 if (st->airport_flags & airport_flags) {
01770 v->cur_speed = 0;
01771 v->subspeed = 0;
01772 return false;
01773 }
01774
01775 if (next->block != NOTHING_block) {
01776 SETBITS(st->airport_flags, airport_flags);
01777 }
01778 }
01779 return true;
01780 }
01781
01782 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01783 {
01784 Station *st = Station::Get(v->targetairport);
01785 for (; i < last_terminal; i++) {
01786 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01787
01788 v->state = _airport_terminal_state[i];
01789 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01790 return true;
01791 }
01792 }
01793 return false;
01794 }
01795
01796 static uint GetNumTerminals(const AirportFTAClass *apc)
01797 {
01798 uint num = 0;
01799
01800 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01801
01802 return num;
01803 }
01804
01805 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01806 {
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817 if (apc->terminals[0] > 1) {
01818 const Station *st = Station::Get(v->targetairport);
01819 const AirportFTA *temp = apc->layout[v->pos].next;
01820
01821 while (temp != NULL) {
01822 if (temp->heading == 255) {
01823 if (!(st->airport_flags & temp->block)) {
01824
01825
01826 uint target_group = temp->next_position + 1;
01827
01828
01829
01830
01831 uint group_start = 0;
01832 for (uint i = 1; i < target_group; i++) {
01833 group_start += apc->terminals[i];
01834 }
01835
01836 uint group_end = group_start + apc->terminals[target_group];
01837 if (FreeTerminal(v, group_start, group_end)) return true;
01838 }
01839 } else {
01840
01841
01842 return false;
01843 }
01844 temp = temp->next;
01845 }
01846 }
01847
01848
01849 return FreeTerminal(v, 0, GetNumTerminals(apc));
01850 }
01851
01852 static uint GetNumHelipads(const AirportFTAClass *apc)
01853 {
01854 uint num = 0;
01855
01856 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01857
01858 return num;
01859 }
01860
01861
01862 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01863 {
01864
01865 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01866
01867
01868 if (apc->helipads[0] > 1) {
01869 const Station *st = Station::Get(v->targetairport);
01870 const AirportFTA *temp = apc->layout[v->pos].next;
01871
01872 while (temp != NULL) {
01873 if (temp->heading == 255) {
01874 if (!(st->airport_flags & temp->block)) {
01875
01876
01877
01878 uint target_group = temp->next_position + 1;
01879
01880
01881
01882
01883 uint group_start = 0;
01884 for (uint i = 1; i < target_group; i++) {
01885 group_start += apc->helipads[i];
01886 }
01887
01888 uint group_end = group_start + apc->helipads[target_group];
01889 if (FreeTerminal(v, group_start, group_end)) return true;
01890 }
01891 } else {
01892
01893
01894 return false;
01895 }
01896 temp = temp->next;
01897 }
01898 } else {
01899
01900
01901 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01902 }
01903 return false;
01904 }
01905
01906 static bool AircraftEventHandler(Aircraft *v, int loop)
01907 {
01908 v->tick_counter++;
01909
01910 if (v->vehstatus & VS_CRASHED) {
01911 return HandleCrashedAircraft(v);
01912 }
01913
01914 if (v->vehstatus & VS_STOPPED) return true;
01915
01916
01917 if (v->breakdown_ctr != 0) {
01918 if (v->breakdown_ctr <= 2) {
01919 HandleBrokenAircraft(v);
01920 } else {
01921 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
01922 }
01923 }
01924
01925 HandleAircraftSmoke(v);
01926 ProcessOrders(v);
01927 v->HandleLoading(loop != 0);
01928
01929 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01930
01931 AirportGoToNextPosition(v);
01932
01933 return true;
01934 }
01935
01936 bool Aircraft::Tick()
01937 {
01938 if (!this->IsNormalAircraft()) return true;
01939
01940 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01941
01942 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01943
01944 this->current_order_time++;
01945
01946 for (uint i = 0; i != 2; i++) {
01947
01948 if (!AircraftEventHandler(this, i)) return false;
01949 }
01950
01951 return true;
01952 }
01953
01954
01960 Station *GetTargetAirportIfValid(const Aircraft *v)
01961 {
01962 assert(v->type == VEH_AIRCRAFT);
01963
01964 Station *st = Station::GetIfValid(v->targetairport);
01965 if (st == NULL) return NULL;
01966
01967 return st->airport_tile == INVALID_TILE ? NULL : st;
01968 }
01969
01974 void UpdateAirplanesOnNewStation(const Station *st)
01975 {
01976
01977 const AirportFTAClass *ap = st->Airport();
01978
01979 Aircraft *v;
01980 FOR_ALL_AIRCRAFT(v) {
01981 if (v->IsNormalAircraft()) {
01982 if (v->targetairport == st->index) {
01983
01984
01985 if (v->state >= FLYING) {
01986 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap);
01987 v->state = FLYING;
01988 UpdateAircraftCache(v);
01989
01990
01991 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
01992
01993 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01994 } else {
01995 assert(v->state == ENDTAKEOFF || v->state == HELITAKEOFF);
01996 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
01997
01998
01999 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02000 if (ap->layout[cnt].heading == takeofftype) {
02001 v->pos = ap->layout[cnt].position;
02002 UpdateAircraftCache(v);
02003 break;
02004 }
02005 }
02006 }
02007 }
02008 }
02009 }
02010 }