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