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
01246 void AircraftLeaveHangar(Aircraft *v)
01247 {
01248 v->cur_speed = 0;
01249 v->subspeed = 0;
01250 v->progress = 0;
01251 v->direction = DIR_SE;
01252 v->vehstatus &= ~VS_HIDDEN;
01253 {
01254 Vehicle *u = v->Next();
01255 u->vehstatus &= ~VS_HIDDEN;
01256
01257
01258 u = u->Next();
01259 if (u != NULL) {
01260 u->vehstatus &= ~VS_HIDDEN;
01261 u->cur_speed = 80;
01262 }
01263 }
01264
01265 VehicleServiceInDepot(v);
01266 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01267 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01268 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01269 }
01270
01274 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01275 {
01276 AircraftEntersTerminal(v);
01277 v->state = apc->layout[v->pos].heading;
01278 }
01279
01285 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01286 {
01287 VehicleEnterDepot(v);
01288 v->state = apc->layout[v->pos].heading;
01289 }
01290
01296 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01297 {
01298
01299 if (v->previous_pos != v->pos) {
01300 AircraftEventHandler_EnterHangar(v, apc);
01301 return;
01302 }
01303
01304
01305 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01306 v->current_order.Free();
01307 return;
01308 }
01309
01310 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01311 !v->current_order.IsType(OT_GOTO_DEPOT))
01312 return;
01313
01314
01315 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01316 VehicleEnterDepot(v);
01317 return;
01318 }
01319
01320
01321 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01322
01323
01324 if (v->current_order.GetDestination() == v->targetairport) {
01325
01326
01327 if (v->subtype == AIR_HELICOPTER) {
01328 if (!AirportFindFreeHelipad(v, apc)) return;
01329 } else {
01330 if (!AirportFindFreeTerminal(v, apc)) return;
01331 }
01332 } else {
01333
01334 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01335 }
01336 AircraftLeaveHangar(v);
01337 AirportMove(v, apc);
01338 }
01339
01341 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01342 {
01343
01344 if (v->previous_pos != v->pos) {
01345 AircraftEventHandler_EnterTerminal(v, apc);
01346
01347
01348 if (_settings_game.order.serviceathelipad) {
01349 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01350
01351 v->date_of_last_service = _date;
01352 v->breakdowns_since_last_service = 0;
01353 v->reliability = Engine::Get(v->engine_type)->reliability;
01354 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01355 }
01356 }
01357 return;
01358 }
01359
01360 if (v->current_order.IsType(OT_NOTHING)) return;
01361
01362
01363 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01364
01365
01366
01367
01368 bool go_to_hangar = false;
01369 switch (v->current_order.GetType()) {
01370 case OT_GOTO_STATION:
01371 break;
01372 case OT_GOTO_DEPOT:
01373 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01374 break;
01375 case OT_CONDITIONAL:
01376
01377
01378
01379 return;
01380 default:
01381 v->current_order.Free();
01382 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01383 }
01384
01385 if (go_to_hangar) {
01386 v->state = HANGAR;
01387 } else {
01388
01389 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01390 }
01391 AirportMove(v, apc);
01392 }
01393
01394 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01395 {
01396 error("OK, you shouldn't be here, check your Airport Scheme!");
01397 }
01398
01399 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01400 {
01401 PlayAircraftSound(v);
01402 v->state = STARTTAKEOFF;
01403 }
01404
01405 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01406 {
01407 v->state = ENDTAKEOFF;
01408 v->UpdateDeltaXY(INVALID_DIR);
01409 }
01410
01411 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01412 {
01413 v->state = FLYING;
01414
01415 AircraftNextAirportPos_and_Order(v);
01416 }
01417
01418 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01419 {
01420 v->state = FLYING;
01421 v->UpdateDeltaXY(INVALID_DIR);
01422
01423
01424 AircraftNextAirportPos_and_Order(v);
01425
01426
01427 if (v->NeedsAutomaticServicing()) {
01428 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01429 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01430 cur_company.Restore();
01431 }
01432 }
01433
01434 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01435 {
01436 Station *st = Station::Get(v->targetairport);
01437
01438
01439 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01440
01441
01442
01443 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01444 const AirportFTA *current = apc->layout[v->pos].next;
01445 while (current != NULL) {
01446 if (current->heading == landingtype) {
01447
01448
01449
01450 uint16 tcur_speed = v->cur_speed;
01451 uint16 tsubspeed = v->subspeed;
01452 if (!AirportHasBlock(v, current, apc)) {
01453 v->state = landingtype;
01454
01455
01456
01457 v->pos = current->next_position;
01458 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01459 return;
01460 }
01461 v->cur_speed = tcur_speed;
01462 v->subspeed = tsubspeed;
01463 }
01464 current = current->next;
01465 }
01466 }
01467 v->state = FLYING;
01468 v->pos = apc->layout[v->pos].next_position;
01469 }
01470
01471 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01472 {
01473 v->state = ENDLANDING;
01474 AircraftLandAirplane(v);
01475
01476
01477 if (v->NeedsAutomaticServicing()) {
01478 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01479 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01480 cur_company.Restore();
01481 }
01482 }
01483
01484 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01485 {
01486 v->state = HELIENDLANDING;
01487 v->UpdateDeltaXY(INVALID_DIR);
01488 }
01489
01490 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01491 {
01492
01493 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01494
01495
01496
01497
01498
01499 if (v->current_order.IsType(OT_GOTO_STATION)) {
01500 if (AirportFindFreeTerminal(v, apc)) return;
01501 }
01502 v->state = HANGAR;
01503
01504 }
01505
01506 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01507 {
01508
01509 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01510
01511
01512
01513
01514
01515
01516
01517
01518 if (v->current_order.IsType(OT_GOTO_STATION)) {
01519 if (AirportFindFreeHelipad(v, apc)) return;
01520 }
01521 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01522 }
01523
01529 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01531 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01532 AircraftEventHandler_General,
01533 AircraftEventHandler_InHangar,
01534 AircraftEventHandler_AtTerminal,
01535 AircraftEventHandler_AtTerminal,
01536 AircraftEventHandler_AtTerminal,
01537 AircraftEventHandler_AtTerminal,
01538 AircraftEventHandler_AtTerminal,
01539 AircraftEventHandler_AtTerminal,
01540 AircraftEventHandler_AtTerminal,
01541 AircraftEventHandler_AtTerminal,
01542 AircraftEventHandler_TakeOff,
01543 AircraftEventHandler_StartTakeOff,
01544 AircraftEventHandler_EndTakeOff,
01545 AircraftEventHandler_HeliTakeOff,
01546 AircraftEventHandler_Flying,
01547 AircraftEventHandler_Landing,
01548 AircraftEventHandler_EndLanding,
01549 AircraftEventHandler_HeliLanding,
01550 AircraftEventHandler_HeliEndLanding,
01551 AircraftEventHandler_AtTerminal,
01552 AircraftEventHandler_AtTerminal,
01553 AircraftEventHandler_AtTerminal,
01554 };
01555
01556 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01557 {
01558
01559 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01560 Station *st = Station::Get(v->targetairport);
01561
01562 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01563 }
01564 }
01565
01566 static void AirportGoToNextPosition(Aircraft *v)
01567 {
01568
01569 if (!AircraftController(v)) return;
01570
01571 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01572
01573 AirportClearBlock(v, apc);
01574 AirportMove(v, apc);
01575 }
01576
01577
01578 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01579 {
01580
01581 if (v->pos >= apc->nofelements) {
01582 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01583 assert(v->pos < apc->nofelements);
01584 }
01585
01586 const AirportFTA *current = &apc->layout[v->pos];
01587
01588 if (current->heading == v->state) {
01589 byte prev_pos = v->pos;
01590 byte prev_state = v->state;
01591 _aircraft_state_handlers[v->state](v, apc);
01592 if (v->state != FLYING) v->previous_pos = prev_pos;
01593 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01594 return true;
01595 }
01596
01597 v->previous_pos = v->pos;
01598
01599
01600 if (current->next == NULL) {
01601 if (AirportSetBlocks(v, current, apc)) {
01602 v->pos = current->next_position;
01603 UpdateAircraftCache(v);
01604 }
01605 return false;
01606 }
01607
01608
01609
01610 do {
01611 if (v->state == current->heading || current->heading == TO_ALL) {
01612 if (AirportSetBlocks(v, current, apc)) {
01613 v->pos = current->next_position;
01614 UpdateAircraftCache(v);
01615 }
01616 return false;
01617 }
01618 current = current->next;
01619 } while (current != NULL);
01620
01621 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01622 NOT_REACHED();
01623 }
01624
01626 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01627 {
01628 const AirportFTA *reference = &apc->layout[v->pos];
01629 const AirportFTA *next = &apc->layout[current_pos->next_position];
01630
01631
01632 if (apc->layout[current_pos->position].block != next->block) {
01633 const Station *st = Station::Get(v->targetairport);
01634 uint64 airport_flags = next->block;
01635
01636
01637 if (current_pos != reference && current_pos->block != NOTHING_block) {
01638 airport_flags |= current_pos->block;
01639 }
01640
01641 if (st->airport.flags & airport_flags) {
01642 v->cur_speed = 0;
01643 v->subspeed = 0;
01644 return true;
01645 }
01646 }
01647 return false;
01648 }
01649
01657 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01658 {
01659 const AirportFTA *next = &apc->layout[current_pos->next_position];
01660 const AirportFTA *reference = &apc->layout[v->pos];
01661
01662
01663 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01664 uint64 airport_flags = next->block;
01665
01666
01667 const AirportFTA *current = current_pos;
01668 if (current == reference) current = current->next;
01669 while (current != NULL) {
01670 if (current->heading == current_pos->heading && current->block != 0) {
01671 airport_flags |= current->block;
01672 break;
01673 }
01674 current = current->next;
01675 }
01676
01677
01678
01679 if (current_pos->block == next->block) airport_flags ^= next->block;
01680
01681 Station *st = Station::Get(v->targetairport);
01682 if (st->airport.flags & airport_flags) {
01683 v->cur_speed = 0;
01684 v->subspeed = 0;
01685 return false;
01686 }
01687
01688 if (next->block != NOTHING_block) {
01689 SETBITS(st->airport.flags, airport_flags);
01690 }
01691 }
01692 return true;
01693 }
01694
01699 struct MovementTerminalMapping {
01700 AirportMovementStates state;
01701 uint64 airport_flag;
01702 };
01703
01705 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01706 {TERM1, TERM1_block},
01707 {TERM2, TERM2_block},
01708 {TERM3, TERM3_block},
01709 {TERM4, TERM4_block},
01710 {TERM5, TERM5_block},
01711 {TERM6, TERM6_block},
01712 {TERM7, TERM7_block},
01713 {TERM8, TERM8_block},
01714 {HELIPAD1, HELIPAD1_block},
01715 {HELIPAD2, HELIPAD2_block},
01716 {HELIPAD3, HELIPAD3_block},
01717 };
01718
01726 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01727 {
01728 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01729 Station *st = Station::Get(v->targetairport);
01730 for (; i < last_terminal; i++) {
01731 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01732
01733 v->state = _airport_terminal_mapping[i].state;
01734 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01735 return true;
01736 }
01737 }
01738 return false;
01739 }
01740
01746 static uint GetNumTerminals(const AirportFTAClass *apc)
01747 {
01748 uint num = 0;
01749
01750 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01751
01752 return num;
01753 }
01754
01761 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01762 {
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773 if (apc->terminals[0] > 1) {
01774 const Station *st = Station::Get(v->targetairport);
01775 const AirportFTA *temp = apc->layout[v->pos].next;
01776
01777 while (temp != NULL) {
01778 if (temp->heading == 255) {
01779 if (!(st->airport.flags & temp->block)) {
01780
01781
01782 uint target_group = temp->next_position + 1;
01783
01784
01785
01786
01787 uint group_start = 0;
01788 for (uint i = 1; i < target_group; i++) {
01789 group_start += apc->terminals[i];
01790 }
01791
01792 uint group_end = group_start + apc->terminals[target_group];
01793 if (FreeTerminal(v, group_start, group_end)) return true;
01794 }
01795 } else {
01796
01797
01798 return false;
01799 }
01800 temp = temp->next;
01801 }
01802 }
01803
01804
01805 return FreeTerminal(v, 0, GetNumTerminals(apc));
01806 }
01807
01814 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01815 {
01816
01817 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01818
01819
01820
01821 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01822 }
01823
01824 static bool AircraftEventHandler(Aircraft *v, int loop)
01825 {
01826 v->tick_counter++;
01827
01828 if (v->vehstatus & VS_CRASHED) {
01829 return HandleCrashedAircraft(v);
01830 }
01831
01832 if (v->vehstatus & VS_STOPPED) return true;
01833
01834 v->HandleBreakdown();
01835
01836 HandleAircraftSmoke(v);
01837 ProcessOrders(v);
01838 v->HandleLoading(loop != 0);
01839
01840 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01841
01842 AirportGoToNextPosition(v);
01843
01844 return true;
01845 }
01846
01847 bool Aircraft::Tick()
01848 {
01849 if (!this->IsNormalAircraft()) return true;
01850
01851 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01852
01853 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01854
01855 this->current_order_time++;
01856
01857 for (uint i = 0; i != 2; i++) {
01858
01859 if (!AircraftEventHandler(this, i)) return false;
01860 }
01861
01862 return true;
01863 }
01864
01865
01872 Station *GetTargetAirportIfValid(const Aircraft *v)
01873 {
01874 assert(v->type == VEH_AIRCRAFT);
01875
01876 Station *st = Station::GetIfValid(v->targetairport);
01877 if (st == NULL) return NULL;
01878
01879 return st->airport.tile == INVALID_TILE ? NULL : st;
01880 }
01881
01886 void UpdateAirplanesOnNewStation(const Station *st)
01887 {
01888
01889 const AirportFTAClass *ap = st->airport.GetFTA();
01890 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01891
01892 Aircraft *v;
01893 FOR_ALL_AIRCRAFT(v) {
01894 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01895 assert(v->state == FLYING);
01896 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01897 UpdateAircraftCache(v);
01898 }
01899 }