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 v->last_loading_station = INVALID_STATION;
00271
00272 v->acceleration = avi->acceleration;
00273 v->engine_type = e->index;
00274 u->engine_type = e->index;
00275
00276 v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00277 v->UpdateDeltaXY(INVALID_DIR);
00278
00279 u->subtype = AIR_SHADOW;
00280 u->UpdateDeltaXY(INVALID_DIR);
00281
00282 v->reliability = e->reliability;
00283 v->reliability_spd_dec = e->reliability_spd_dec;
00284 v->max_age = e->GetLifeLengthInDays();
00285
00286 _new_vehicle_id = v->index;
00287
00288 v->pos = GetVehiclePosOnBuild(tile);
00289
00290 v->state = HANGAR;
00291 v->previous_pos = v->pos;
00292 v->targetairport = GetStationIndex(tile);
00293 v->SetNext(u);
00294
00295 v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_aircraft;
00296
00297 v->date_of_last_service = _date;
00298 v->build_year = u->build_year = _cur_year;
00299
00300 v->cur_image = u->cur_image = SPR_IMG_QUERY;
00301
00302 v->random_bits = VehicleRandomBits();
00303 u->random_bits = VehicleRandomBits();
00304
00305 v->vehicle_flags = 0;
00306 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00307
00308 v->InvalidateNewGRFCacheOfChain();
00309
00310 v->cargo_cap = GetVehicleCapacity(v, &u->cargo_cap);
00311
00312 v->InvalidateNewGRFCacheOfChain();
00313
00314 UpdateAircraftCache(v);
00315
00316 VehicleMove(v, false);
00317 VehicleMove(u, false);
00318
00319
00320 if (v->subtype == AIR_HELICOPTER) {
00321 Aircraft *w = new Aircraft();
00322 w->engine_type = e->index;
00323 w->direction = DIR_N;
00324 w->owner = _current_company;
00325 w->x_pos = v->x_pos;
00326 w->y_pos = v->y_pos;
00327 w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
00328 w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00329 w->spritenum = 0xFF;
00330 w->subtype = AIR_ROTOR;
00331 w->cur_image = SPR_ROTOR_STOPPED;
00332 w->random_bits = VehicleRandomBits();
00333
00334 w->state = HRS_ROTOR_STOPPED;
00335 w->UpdateDeltaXY(INVALID_DIR);
00336
00337 u->SetNext(w);
00338 VehicleMove(w, false);
00339 }
00340 }
00341
00342 return CommandCost();
00343 }
00344
00345
00346 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00347 {
00348 const Station *st = GetTargetAirportIfValid(this);
00349
00350 if (st == NULL || !st->airport.HasHangar()) {
00351
00352 StationID station = FindNearestHangar(this);
00353
00354 if (station == INVALID_STATION) return false;
00355
00356 st = Station::Get(station);
00357 }
00358
00359 if (location != NULL) *location = st->xy;
00360 if (destination != NULL) *destination = st->index;
00361
00362 return true;
00363 }
00364
00365 static void CheckIfAircraftNeedsService(Aircraft *v)
00366 {
00367 if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00368 if (v->IsInDepot()) {
00369 VehicleServiceInDepot(v);
00370 return;
00371 }
00372
00373
00374
00375 if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
00376
00377 const Station *st = Station::Get(v->current_order.GetDestination());
00378
00379 assert(st != NULL);
00380
00381
00382 if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
00383 v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00384 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00385 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00386 v->current_order.MakeDummy();
00387 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00388 }
00389 }
00390
00391 Money Aircraft::GetRunningCost() const
00392 {
00393 const Engine *e = Engine::Get(this->engine_type);
00394 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00395 return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->grf_prop.grffile);
00396 }
00397
00398 void Aircraft::OnNewDay()
00399 {
00400 if (!this->IsNormalAircraft()) return;
00401
00402 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00403
00404 CheckOrders(this);
00405
00406 CheckVehicleBreakdown(this);
00407 AgeVehicle(this);
00408 CheckIfAircraftNeedsService(this);
00409
00410 if (this->running_ticks == 0) return;
00411
00412 CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00413
00414 this->profit_this_year -= cost.GetCost();
00415 this->running_ticks = 0;
00416
00417 SubtractMoneyFromCompanyFract(this->owner, cost);
00418
00419 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00420 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00421 }
00422
00423 static void HelicopterTickHandler(Aircraft *v)
00424 {
00425 Aircraft *u = v->Next()->Next();
00426
00427 if (u->vehstatus & VS_HIDDEN) return;
00428
00429
00430
00431 if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00432 if (u->cur_speed != 0) {
00433 u->cur_speed++;
00434 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00435 u->cur_speed = 0;
00436 }
00437 }
00438 } else {
00439 if (u->cur_speed == 0) {
00440 u->cur_speed = 0x70;
00441 }
00442 if (u->cur_speed >= 0x50) {
00443 u->cur_speed--;
00444 }
00445 }
00446
00447 int tick = ++u->tick_counter;
00448 int spd = u->cur_speed >> 4;
00449
00450 SpriteID img;
00451 if (spd == 0) {
00452 u->state = HRS_ROTOR_STOPPED;
00453 img = GetRotorImage(v);
00454 if (u->cur_image == img) return;
00455 } else if (tick >= spd) {
00456 u->tick_counter = 0;
00457 u->state++;
00458 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00459 img = GetRotorImage(v);
00460 } else {
00461 return;
00462 }
00463
00464 u->cur_image = img;
00465
00466 VehicleMove(u, true);
00467 }
00468
00476 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00477 {
00478 v->x_pos = x;
00479 v->y_pos = y;
00480 v->z_pos = z;
00481
00482 v->UpdateViewport(true, false);
00483 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00484
00485 Aircraft *u = v->Next();
00486
00487 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00488 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00489 u->x_pos = x;
00490 u->y_pos = y - ((v->z_pos - GetSlopeZ(safe_x, safe_y)) >> 3);
00491
00492 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00493 u->z_pos = GetSlopeZ(safe_x, safe_y);
00494 u->cur_image = v->cur_image;
00495
00496 VehicleMove(u, true);
00497
00498 u = u->Next();
00499 if (u != NULL) {
00500 u->x_pos = x;
00501 u->y_pos = y;
00502 u->z_pos = z + ROTOR_Z_OFFSET;
00503
00504 VehicleMove(u, true);
00505 }
00506 }
00507
00512 void HandleAircraftEnterHangar(Aircraft *v)
00513 {
00514 v->subspeed = 0;
00515 v->progress = 0;
00516
00517 Aircraft *u = v->Next();
00518 u->vehstatus |= VS_HIDDEN;
00519 u = u->Next();
00520 if (u != NULL) {
00521 u->vehstatus |= VS_HIDDEN;
00522 u->cur_speed = 0;
00523 }
00524
00525 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00526 }
00527
00528 static void PlayAircraftSound(const Vehicle *v)
00529 {
00530 if (!PlayVehicleSound(v, VSE_START)) {
00531 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00532 }
00533 }
00534
00535
00541 void UpdateAircraftCache(Aircraft *v)
00542 {
00543 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00544 if (max_speed != 0) {
00545
00546 max_speed = (max_speed * 128) / 10;
00547
00548 v->vcache.cached_max_speed = max_speed;
00549 } else {
00550
00551 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00552 }
00553 }
00554
00555
00559 enum AircraftSpeedLimits {
00560 SPEED_LIMIT_TAXI = 50,
00561 SPEED_LIMIT_APPROACH = 230,
00562 SPEED_LIMIT_BROKEN = 320,
00563 SPEED_LIMIT_HOLD = 425,
00564 SPEED_LIMIT_NONE = 0xFFFF
00565 };
00566
00574 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00575 {
00576 uint spd = v->acceleration * 16;
00577 byte t;
00578
00579
00580
00581 speed_limit *= _settings_game.vehicle.plane_speed;
00582
00583 if (v->vcache.cached_max_speed < speed_limit) {
00584 if (v->cur_speed < speed_limit) hard_limit = false;
00585 speed_limit = v->vcache.cached_max_speed;
00586 }
00587
00588 v->subspeed = (t = v->subspeed) + (byte)spd;
00589
00590
00591
00592
00593
00594
00595
00596 if (!hard_limit && v->cur_speed > speed_limit) {
00597 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00598 }
00599
00600 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00601
00602
00603 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00604
00605
00606 if (spd != v->cur_speed) {
00607 v->cur_speed = spd;
00608 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00609 }
00610
00611
00612 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00613
00614
00615 spd = v->GetOldAdvanceSpeed(spd);
00616
00617 spd += v->progress;
00618 v->progress = (byte)spd;
00619 return spd >> 8;
00620 }
00621
00629 byte GetAircraftFlyingAltitude(const Aircraft *v)
00630 {
00631 if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00632
00633
00634
00635
00636 byte base_altitude = PLANE_HOLDING_ALTITUDE;
00637
00638
00639
00640
00641 switch (v->direction) {
00642 case DIR_N:
00643 case DIR_NE:
00644 case DIR_E:
00645 case DIR_SE:
00646 base_altitude += 10;
00647 break;
00648
00649 default: break;
00650 }
00651
00652
00653 base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00654
00655 return base_altitude;
00656 }
00657
00672 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00673 {
00674 assert(v != NULL);
00675 assert(apc != NULL);
00676
00677
00678
00679
00680 TileIndex tile = 0;
00681
00682 const Station *st = Station::GetIfValid(v->targetairport);
00683 if (st != NULL) {
00684
00685 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00686 }
00687
00688 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00689 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00690
00691 DiagDirection dir;
00692 if (abs(delta_y) < abs(delta_x)) {
00693
00694 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00695 } else {
00696
00697 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00698 }
00699 dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00700 return apc->entry_points[dir];
00701 }
00702
00703
00704 static void MaybeCrashAirplane(Aircraft *v);
00705
00713 static bool AircraftController(Aircraft *v)
00714 {
00715 int count;
00716
00717
00718 const Station *st = Station::GetIfValid(v->targetairport);
00719
00720 TileIndex tile = INVALID_TILE;
00721 Direction rotation = DIR_N;
00722 uint size_x = 1, size_y = 1;
00723 if (st != NULL) {
00724 if (st->airport.tile != INVALID_TILE) {
00725 tile = st->airport.tile;
00726 rotation = st->airport.rotation;
00727 size_x = st->airport.w;
00728 size_y = st->airport.h;
00729 } else {
00730 tile = st->xy;
00731 }
00732 }
00733
00734 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00735
00736
00737 if (st == NULL || st->airport.tile == INVALID_TILE) {
00738
00739 if (v->pos >= afc->nofelements) {
00740 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00741 } else if (v->targetairport != v->current_order.GetDestination()) {
00742
00743 v->state = FLYING;
00744 UpdateAircraftCache(v);
00745 AircraftNextAirportPos_and_Order(v);
00746
00747 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00748 return false;
00749 }
00750 }
00751
00752
00753 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00754
00755 int x = TileX(tile) * TILE_SIZE;
00756 int y = TileY(tile) * TILE_SIZE;
00757
00758
00759 if (amd.flag & AMED_HELI_RAISE) {
00760 Aircraft *u = v->Next()->Next();
00761
00762
00763 if (u->cur_speed > 32) {
00764 v->cur_speed = 0;
00765 if (--u->cur_speed == 32) {
00766 if (!PlayVehicleSound(v, VSE_START)) {
00767 SndPlayVehicleFx(SND_18_HELICOPTER, v);
00768 }
00769 }
00770 } else {
00771 u->cur_speed = 32;
00772 count = UpdateAircraftSpeed(v);
00773 if (count > 0) {
00774 v->tile = 0;
00775 byte z_dest = GetAircraftFlyingAltitude(v);
00776
00777
00778 if (v->z_pos >= z_dest) {
00779 v->cur_speed = 0;
00780 return true;
00781 }
00782 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00783 }
00784 }
00785 return false;
00786 }
00787
00788
00789 if (amd.flag & AMED_HELI_LOWER) {
00790 if (st == NULL) {
00791
00792
00793
00794 v->state = FLYING;
00795 UpdateAircraftCache(v);
00796 AircraftNextAirportPos_and_Order(v);
00797 return false;
00798 }
00799
00800
00801 v->tile = tile;
00802
00803
00804 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00805
00806 if (z == v->z_pos) {
00807 Vehicle *u = v->Next()->Next();
00808
00809
00810 if (u->cur_speed >= 80) return true;
00811 u->cur_speed += 4;
00812 } else {
00813 count = UpdateAircraftSpeed(v);
00814 if (count > 0) {
00815 if (v->z_pos > z) {
00816 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00817 } else {
00818 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00819 }
00820 }
00821 }
00822 return false;
00823 }
00824
00825
00826 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
00827
00828
00829 if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00830
00831
00832 if (dist == 0) {
00833
00834 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00835
00836
00837 if (dirdiff == DIRDIFF_SAME) {
00838 v->cur_speed = 0;
00839 return true;
00840 }
00841
00842 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00843
00844 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00845 v->cur_speed >>= 1;
00846
00847 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00848 return false;
00849 }
00850
00851 if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00852 MaybeCrashAirplane(v);
00853 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00854 }
00855
00856 uint speed_limit = SPEED_LIMIT_TAXI;
00857 bool hard_limit = true;
00858
00859 if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00860 if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00861 if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00862 if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00863
00864 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00865 if (count == 0) return false;
00866
00867 if (v->turn_counter != 0) v->turn_counter--;
00868
00869 do {
00870
00871 GetNewVehiclePosResult gp;
00872
00873 if (dist < 4 || (amd.flag & AMED_LAND)) {
00874
00875 gp.x = (v->x_pos != (x + amd.x)) ?
00876 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00877 v->x_pos;
00878 gp.y = (v->y_pos != (y + amd.y)) ?
00879 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00880 v->y_pos;
00881
00882
00883 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00884
00885 } else {
00886
00887
00888 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00889 if (newdir != v->direction) {
00890 if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00891 if (v->turn_counter == 0 || newdir == v->last_direction) {
00892 if (newdir == v->last_direction) {
00893 v->number_consecutive_turns = 0;
00894 } else {
00895 v->number_consecutive_turns++;
00896 }
00897 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00898 v->last_direction = v->direction;
00899 v->direction = newdir;
00900 }
00901
00902
00903 gp = GetNewVehiclePos(v);
00904 } else {
00905 v->cur_speed >>= 1;
00906 v->direction = newdir;
00907
00908
00909
00910
00911
00912
00913 gp.x = v->x_pos;
00914 gp.y = v->y_pos;
00915 gp.new_tile = gp.old_tile = v->tile;
00916 }
00917 } else {
00918 v->number_consecutive_turns = 0;
00919
00920 gp = GetNewVehiclePos(v);
00921 }
00922 }
00923
00924 v->tile = gp.new_tile;
00925
00926 if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00927
00928
00929 uint z = v->z_pos;
00930
00931 if (amd.flag & AMED_TAKEOFF) {
00932 z = min(z + 2, GetAircraftFlyingAltitude(v));
00933 }
00934
00935
00936 if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00937
00938 if (amd.flag & AMED_LAND) {
00939 if (st->airport.tile == INVALID_TILE) {
00940
00941 v->state = FLYING;
00942 UpdateAircraftCache(v);
00943 AircraftNextAirportPos_and_Order(v);
00944
00945 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00946 continue;
00947 }
00948
00949 uint curz = GetSlopeZ(x + amd.x, y + amd.y) + 1;
00950
00951
00952 assert(curz <= z);
00953 int t = max(1U, dist - 4);
00954 int delta = z - curz;
00955
00956
00957 if (delta >= t) {
00958 z -= CeilDiv(z - curz, t);
00959 }
00960 if (z < curz) z = curz;
00961 }
00962
00963
00964 if (amd.flag & AMED_BRAKE) {
00965 uint curz = GetSlopeZ(x, y) + 1;
00966
00967 if (z > curz) {
00968 z--;
00969 } else if (z < curz) {
00970 z++;
00971 }
00972
00973 }
00974
00975 SetAircraftPosition(v, gp.x, gp.y, z);
00976 } while (--count != 0);
00977 return false;
00978 }
00979
00984 static bool HandleCrashedAircraft(Aircraft *v)
00985 {
00986 v->crashed_counter += 3;
00987
00988 Station *st = GetTargetAirportIfValid(v);
00989
00990
00991 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
00992 uint z = GetSlopeZ(v->x_pos, v->y_pos);
00993 v->z_pos -= 1;
00994 if (v->z_pos == z) {
00995 v->crashed_counter = 500;
00996 v->z_pos++;
00997 }
00998 }
00999
01000 if (v->crashed_counter < 650) {
01001 uint32 r;
01002 if (Chance16R(1, 32, r)) {
01003 static const DirDiff delta[] = {
01004 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01005 };
01006
01007 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01008 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01009 r = Random();
01010 CreateEffectVehicleRel(v,
01011 GB(r, 0, 4) - 4,
01012 GB(r, 4, 4) - 4,
01013 GB(r, 8, 4),
01014 EV_EXPLOSION_SMALL);
01015 }
01016 } else if (v->crashed_counter >= 10000) {
01017
01018
01019
01020
01021
01022 if (st != NULL) {
01023 CLRBITS(st->airport.flags, RUNWAY_IN_block);
01024 CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block);
01025 CLRBITS(st->airport.flags, RUNWAY_IN2_block);
01026 }
01027
01028 delete v;
01029
01030 return false;
01031 }
01032
01033 return true;
01034 }
01035
01036
01037 static void HandleAircraftSmoke(Aircraft *v)
01038 {
01039 static const struct {
01040 int8 x;
01041 int8 y;
01042 } smoke_pos[] = {
01043 { 5, 5 },
01044 { 6, 0 },
01045 { 5, -5 },
01046 { 0, -6 },
01047 { -5, -5 },
01048 { -6, 0 },
01049 { -5, 5 },
01050 { 0, 6 }
01051 };
01052
01053 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01054
01055 if (v->cur_speed < 10) {
01056 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01057 v->breakdown_ctr = 0;
01058 return;
01059 }
01060
01061 if ((v->tick_counter & 0x1F) == 0) {
01062 CreateEffectVehicleRel(v,
01063 smoke_pos[v->direction].x,
01064 smoke_pos[v->direction].y,
01065 2,
01066 EV_BREAKDOWN_SMOKE_AIRCRAFT
01067 );
01068 }
01069 }
01070
01071 void HandleMissingAircraftOrders(Aircraft *v)
01072 {
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088 const Station *st = GetTargetAirportIfValid(v);
01089 if (st == NULL) {
01090 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01091 CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01092 cur_company.Restore();
01093
01094 if (ret.Failed()) CrashAirplane(v);
01095 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01096 v->current_order.Free();
01097 }
01098 }
01099
01100
01101 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01102 {
01103
01104 if (this->state == FLYING) {
01105 AircraftNextAirportPos_and_Order(this);
01106 }
01107
01108
01109 return 0;
01110 }
01111
01112 void Aircraft::MarkDirty()
01113 {
01114 this->UpdateViewport(false, false);
01115 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01116 }
01117
01118
01119 uint Aircraft::Crash(bool flooded)
01120 {
01121 uint pass = Vehicle::Crash(flooded) + 2;
01122 this->crashed_counter = flooded ? 9000 : 0;
01123
01124 return pass;
01125 }
01126
01131 static void CrashAirplane(Aircraft *v)
01132 {
01133 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01134
01135 uint pass = v->Crash();
01136 SetDParam(0, pass);
01137
01138 v->cargo.Truncate(0);
01139 v->Next()->cargo.Truncate(0);
01140 const Station *st = GetTargetAirportIfValid(v);
01141 StringID newsitem;
01142 if (st == NULL) {
01143 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01144 } else {
01145 SetDParam(1, st->index);
01146 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01147 }
01148
01149 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, st == NULL ? AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : AIEventVehicleCrashed::CRASH_PLANE_LANDING));
01150
01151 AddVehicleNewsItem(newsitem,
01152 NS_ACCIDENT,
01153 v->index,
01154 st != NULL ? st->index : INVALID_STATION);
01155
01156 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01157 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01158 }
01159
01164 static void MaybeCrashAirplane(Aircraft *v)
01165 {
01166 if (_settings_game.vehicle.plane_crashes == 0) return;
01167
01168 Station *st = Station::Get(v->targetairport);
01169
01170
01171 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01172 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01173 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01174 !_cheats.no_jetcrash.value) {
01175 prob /= 20;
01176 } else {
01177 prob /= 1500;
01178 }
01179
01180 if (GB(Random(), 0, 22) > prob) return;
01181
01182
01183 for (CargoID i = 0; i < NUM_CARGO; i++) {
01184 st->goods[i].rating = 1;
01185 st->goods[i].cargo.Truncate(0);
01186 }
01187
01188 CrashAirplane(v);
01189 }
01190
01196 static void AircraftEntersTerminal(Aircraft *v)
01197 {
01198 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01199
01200 Station *st = Station::Get(v->targetairport);
01201 v->last_station_visited = v->targetairport;
01202
01203
01204 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01205 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01206 SetDParam(0, st->index);
01207
01208 AddVehicleNewsItem(
01209 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01210 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01211 v->index,
01212 st->index
01213 );
01214 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01215 }
01216
01217 v->BeginLoading();
01218 }
01219
01224 static void AircraftLandAirplane(Aircraft *v)
01225 {
01226 v->UpdateDeltaXY(INVALID_DIR);
01227
01228 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01229 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01230 }
01231 }
01232
01233
01235 void AircraftNextAirportPos_and_Order(Aircraft *v)
01236 {
01237 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01238 v->targetairport = v->current_order.GetDestination();
01239 }
01240
01241 const Station *st = GetTargetAirportIfValid(v);
01242 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01243 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01244 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01245 }
01246
01247 void AircraftLeaveHangar(Aircraft *v)
01248 {
01249 v->cur_speed = 0;
01250 v->subspeed = 0;
01251 v->progress = 0;
01252 v->direction = DIR_SE;
01253 v->vehstatus &= ~VS_HIDDEN;
01254 {
01255 Vehicle *u = v->Next();
01256 u->vehstatus &= ~VS_HIDDEN;
01257
01258
01259 u = u->Next();
01260 if (u != NULL) {
01261 u->vehstatus &= ~VS_HIDDEN;
01262 u->cur_speed = 80;
01263 }
01264 }
01265
01266 VehicleServiceInDepot(v);
01267 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01268 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01269 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01270 }
01271
01275 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01276 {
01277 AircraftEntersTerminal(v);
01278 v->state = apc->layout[v->pos].heading;
01279 }
01280
01286 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01287 {
01288 VehicleEnterDepot(v);
01289 v->state = apc->layout[v->pos].heading;
01290 }
01291
01297 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01298 {
01299
01300 if (v->previous_pos != v->pos) {
01301 AircraftEventHandler_EnterHangar(v, apc);
01302 return;
01303 }
01304
01305
01306 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01307 v->current_order.Free();
01308 return;
01309 }
01310
01311 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01312 !v->current_order.IsType(OT_GOTO_DEPOT))
01313 return;
01314
01315
01316 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01317 VehicleEnterDepot(v);
01318 return;
01319 }
01320
01321
01322 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01323
01324
01325 if (v->current_order.GetDestination() == v->targetairport) {
01326
01327
01328 if (v->subtype == AIR_HELICOPTER) {
01329 if (!AirportFindFreeHelipad(v, apc)) return;
01330 } else {
01331 if (!AirportFindFreeTerminal(v, apc)) return;
01332 }
01333 } else {
01334
01335 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01336 }
01337 AircraftLeaveHangar(v);
01338 AirportMove(v, apc);
01339 }
01340
01342 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01343 {
01344
01345 if (v->previous_pos != v->pos) {
01346 AircraftEventHandler_EnterTerminal(v, apc);
01347
01348
01349 if (_settings_game.order.serviceathelipad) {
01350 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01351
01352 v->date_of_last_service = _date;
01353 v->breakdowns_since_last_service = 0;
01354 v->reliability = Engine::Get(v->engine_type)->reliability;
01355 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01356 }
01357 }
01358 return;
01359 }
01360
01361 if (v->current_order.IsType(OT_NOTHING)) return;
01362
01363
01364 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01365
01366
01367
01368
01369 bool go_to_hangar = false;
01370 switch (v->current_order.GetType()) {
01371 case OT_GOTO_STATION:
01372 break;
01373 case OT_GOTO_DEPOT:
01374 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01375 break;
01376 case OT_CONDITIONAL:
01377
01378
01379
01380 return;
01381 default:
01382 v->current_order.Free();
01383 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01384 }
01385
01386 if (go_to_hangar) {
01387 v->state = HANGAR;
01388 } else {
01389
01390 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01391 }
01392 AirportMove(v, apc);
01393 }
01394
01395 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01396 {
01397 error("OK, you shouldn't be here, check your Airport Scheme!");
01398 }
01399
01400 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01401 {
01402 PlayAircraftSound(v);
01403 v->state = STARTTAKEOFF;
01404 }
01405
01406 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01407 {
01408 v->state = ENDTAKEOFF;
01409 v->UpdateDeltaXY(INVALID_DIR);
01410 }
01411
01412 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01413 {
01414 v->state = FLYING;
01415
01416 AircraftNextAirportPos_and_Order(v);
01417 }
01418
01419 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01420 {
01421 v->state = FLYING;
01422 v->UpdateDeltaXY(INVALID_DIR);
01423
01424
01425 AircraftNextAirportPos_and_Order(v);
01426
01427
01428 if (v->NeedsAutomaticServicing()) {
01429 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01430 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01431 cur_company.Restore();
01432 }
01433 }
01434
01435 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01436 {
01437 Station *st = Station::Get(v->targetairport);
01438
01439
01440 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01441
01442
01443
01444 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01445 const AirportFTA *current = apc->layout[v->pos].next;
01446 while (current != NULL) {
01447 if (current->heading == landingtype) {
01448
01449
01450
01451 uint16 tcur_speed = v->cur_speed;
01452 uint16 tsubspeed = v->subspeed;
01453 if (!AirportHasBlock(v, current, apc)) {
01454 v->state = landingtype;
01455
01456
01457
01458 v->pos = current->next_position;
01459 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01460 return;
01461 }
01462 v->cur_speed = tcur_speed;
01463 v->subspeed = tsubspeed;
01464 }
01465 current = current->next;
01466 }
01467 }
01468 v->state = FLYING;
01469 v->pos = apc->layout[v->pos].next_position;
01470 }
01471
01472 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01473 {
01474 v->state = ENDLANDING;
01475 AircraftLandAirplane(v);
01476
01477
01478 if (v->NeedsAutomaticServicing()) {
01479 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01480 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01481 cur_company.Restore();
01482 }
01483 }
01484
01485 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01486 {
01487 v->state = HELIENDLANDING;
01488 v->UpdateDeltaXY(INVALID_DIR);
01489 }
01490
01491 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01492 {
01493
01494 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01495
01496
01497
01498
01499
01500 if (v->current_order.IsType(OT_GOTO_STATION)) {
01501 if (AirportFindFreeTerminal(v, apc)) return;
01502 }
01503 v->state = HANGAR;
01504
01505 }
01506
01507 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01508 {
01509
01510 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01511
01512
01513
01514
01515
01516
01517
01518
01519 if (v->current_order.IsType(OT_GOTO_STATION)) {
01520 if (AirportFindFreeHelipad(v, apc)) return;
01521 }
01522 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01523 }
01524
01530 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01532 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01533 AircraftEventHandler_General,
01534 AircraftEventHandler_InHangar,
01535 AircraftEventHandler_AtTerminal,
01536 AircraftEventHandler_AtTerminal,
01537 AircraftEventHandler_AtTerminal,
01538 AircraftEventHandler_AtTerminal,
01539 AircraftEventHandler_AtTerminal,
01540 AircraftEventHandler_AtTerminal,
01541 AircraftEventHandler_AtTerminal,
01542 AircraftEventHandler_AtTerminal,
01543 AircraftEventHandler_TakeOff,
01544 AircraftEventHandler_StartTakeOff,
01545 AircraftEventHandler_EndTakeOff,
01546 AircraftEventHandler_HeliTakeOff,
01547 AircraftEventHandler_Flying,
01548 AircraftEventHandler_Landing,
01549 AircraftEventHandler_EndLanding,
01550 AircraftEventHandler_HeliLanding,
01551 AircraftEventHandler_HeliEndLanding,
01552 AircraftEventHandler_AtTerminal,
01553 AircraftEventHandler_AtTerminal,
01554 AircraftEventHandler_AtTerminal,
01555 };
01556
01557 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01558 {
01559
01560 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01561 Station *st = Station::Get(v->targetairport);
01562
01563 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01564 }
01565 }
01566
01567 static void AirportGoToNextPosition(Aircraft *v)
01568 {
01569
01570 if (!AircraftController(v)) return;
01571
01572 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01573
01574 AirportClearBlock(v, apc);
01575 AirportMove(v, apc);
01576 }
01577
01578
01579 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01580 {
01581
01582 if (v->pos >= apc->nofelements) {
01583 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01584 assert(v->pos < apc->nofelements);
01585 }
01586
01587 const AirportFTA *current = &apc->layout[v->pos];
01588
01589 if (current->heading == v->state) {
01590 byte prev_pos = v->pos;
01591 byte prev_state = v->state;
01592 _aircraft_state_handlers[v->state](v, apc);
01593 if (v->state != FLYING) v->previous_pos = prev_pos;
01594 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01595 return true;
01596 }
01597
01598 v->previous_pos = v->pos;
01599
01600
01601 if (current->next == NULL) {
01602 if (AirportSetBlocks(v, current, apc)) {
01603 v->pos = current->next_position;
01604 UpdateAircraftCache(v);
01605 }
01606 return false;
01607 }
01608
01609
01610
01611 do {
01612 if (v->state == current->heading || current->heading == TO_ALL) {
01613 if (AirportSetBlocks(v, current, apc)) {
01614 v->pos = current->next_position;
01615 UpdateAircraftCache(v);
01616 }
01617 return false;
01618 }
01619 current = current->next;
01620 } while (current != NULL);
01621
01622 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01623 NOT_REACHED();
01624 }
01625
01627 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01628 {
01629 const AirportFTA *reference = &apc->layout[v->pos];
01630 const AirportFTA *next = &apc->layout[current_pos->next_position];
01631
01632
01633 if (apc->layout[current_pos->position].block != next->block) {
01634 const Station *st = Station::Get(v->targetairport);
01635 uint64 airport_flags = next->block;
01636
01637
01638 if (current_pos != reference && current_pos->block != NOTHING_block) {
01639 airport_flags |= current_pos->block;
01640 }
01641
01642 if (st->airport.flags & airport_flags) {
01643 v->cur_speed = 0;
01644 v->subspeed = 0;
01645 return true;
01646 }
01647 }
01648 return false;
01649 }
01650
01658 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01659 {
01660 const AirportFTA *next = &apc->layout[current_pos->next_position];
01661 const AirportFTA *reference = &apc->layout[v->pos];
01662
01663
01664 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01665 uint64 airport_flags = next->block;
01666
01667
01668 const AirportFTA *current = current_pos;
01669 if (current == reference) current = current->next;
01670 while (current != NULL) {
01671 if (current->heading == current_pos->heading && current->block != 0) {
01672 airport_flags |= current->block;
01673 break;
01674 }
01675 current = current->next;
01676 }
01677
01678
01679
01680 if (current_pos->block == next->block) airport_flags ^= next->block;
01681
01682 Station *st = Station::Get(v->targetairport);
01683 if (st->airport.flags & airport_flags) {
01684 v->cur_speed = 0;
01685 v->subspeed = 0;
01686 return false;
01687 }
01688
01689 if (next->block != NOTHING_block) {
01690 SETBITS(st->airport.flags, airport_flags);
01691 }
01692 }
01693 return true;
01694 }
01695
01700 struct MovementTerminalMapping {
01701 AirportMovementStates state;
01702 uint64 airport_flag;
01703 };
01704
01706 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01707 {TERM1, TERM1_block},
01708 {TERM2, TERM2_block},
01709 {TERM3, TERM3_block},
01710 {TERM4, TERM4_block},
01711 {TERM5, TERM5_block},
01712 {TERM6, TERM6_block},
01713 {TERM7, TERM7_block},
01714 {TERM8, TERM8_block},
01715 {HELIPAD1, HELIPAD1_block},
01716 {HELIPAD2, HELIPAD2_block},
01717 {HELIPAD3, HELIPAD3_block},
01718 };
01719
01727 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01728 {
01729 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01730 Station *st = Station::Get(v->targetairport);
01731 for (; i < last_terminal; i++) {
01732 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01733
01734 v->state = _airport_terminal_mapping[i].state;
01735 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01736 return true;
01737 }
01738 }
01739 return false;
01740 }
01741
01747 static uint GetNumTerminals(const AirportFTAClass *apc)
01748 {
01749 uint num = 0;
01750
01751 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01752
01753 return num;
01754 }
01755
01762 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01763 {
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774 if (apc->terminals[0] > 1) {
01775 const Station *st = Station::Get(v->targetairport);
01776 const AirportFTA *temp = apc->layout[v->pos].next;
01777
01778 while (temp != NULL) {
01779 if (temp->heading == 255) {
01780 if (!(st->airport.flags & temp->block)) {
01781
01782
01783 uint target_group = temp->next_position + 1;
01784
01785
01786
01787
01788 uint group_start = 0;
01789 for (uint i = 1; i < target_group; i++) {
01790 group_start += apc->terminals[i];
01791 }
01792
01793 uint group_end = group_start + apc->terminals[target_group];
01794 if (FreeTerminal(v, group_start, group_end)) return true;
01795 }
01796 } else {
01797
01798
01799 return false;
01800 }
01801 temp = temp->next;
01802 }
01803 }
01804
01805
01806 return FreeTerminal(v, 0, GetNumTerminals(apc));
01807 }
01808
01815 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01816 {
01817
01818 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01819
01820
01821
01822 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01823 }
01824
01825 static bool AircraftEventHandler(Aircraft *v, int loop)
01826 {
01827 v->tick_counter++;
01828
01829 if (v->vehstatus & VS_CRASHED) {
01830 return HandleCrashedAircraft(v);
01831 }
01832
01833 if (v->vehstatus & VS_STOPPED) return true;
01834
01835 v->HandleBreakdown();
01836
01837 HandleAircraftSmoke(v);
01838 ProcessOrders(v);
01839 v->HandleLoading(loop != 0);
01840
01841 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01842
01843 AirportGoToNextPosition(v);
01844
01845 return true;
01846 }
01847
01848 bool Aircraft::Tick()
01849 {
01850 if (!this->IsNormalAircraft()) return true;
01851
01852 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01853
01854 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01855
01856 this->current_order_time++;
01857
01858 for (uint i = 0; i != 2; i++) {
01859
01860 if (!AircraftEventHandler(this, i)) return false;
01861 }
01862
01863 return true;
01864 }
01865
01866
01873 Station *GetTargetAirportIfValid(const Aircraft *v)
01874 {
01875 assert(v->type == VEH_AIRCRAFT);
01876
01877 Station *st = Station::GetIfValid(v->targetairport);
01878 if (st == NULL) return NULL;
01879
01880 return st->airport.tile == INVALID_TILE ? NULL : st;
01881 }
01882
01887 void UpdateAirplanesOnNewStation(const Station *st)
01888 {
01889
01890 const AirportFTAClass *ap = st->airport.GetFTA();
01891 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01892
01893 Aircraft *v;
01894 FOR_ALL_AIRCRAFT(v) {
01895 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01896 assert(v->state == FLYING);
01897 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01898 UpdateAircraftCache(v);
01899 }
01900 }