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