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