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 #include "cargotype.h"
00039
00040 #include "table/strings.h"
00041
00042 static const uint ROTOR_Z_OFFSET = 5;
00043
00044 static const uint PLANE_HOLDING_ALTITUDE = 150;
00045 static const uint HELI_FLIGHT_ALTITUDE = 184;
00046
00047
00048 void Aircraft::UpdateDeltaXY(Direction direction)
00049 {
00050 this->x_offs = -1;
00051 this->y_offs = -1;
00052 this->x_extent = 2;
00053 this->y_extent = 2;
00054
00055 switch (this->subtype) {
00056 default: NOT_REACHED();
00057
00058 case AIR_AIRCRAFT:
00059 case AIR_HELICOPTER:
00060 switch (this->state) {
00061 default: break;
00062 case ENDTAKEOFF:
00063 case LANDING:
00064 case HELILANDING:
00065 case FLYING:
00066 this->x_extent = 24;
00067 this->y_extent = 24;
00068 break;
00069 }
00070 this->z_extent = 5;
00071 break;
00072
00073 case AIR_SHADOW:
00074 this->z_extent = 1;
00075 this->x_offs = 0;
00076 this->y_offs = 0;
00077 break;
00078
00079 case AIR_ROTOR:
00080 this->z_extent = 1;
00081 break;
00082 }
00083 }
00084
00085 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc);
00086 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00087 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc);
00088 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc);
00089 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc);
00090 static void CrashAirplane(Aircraft *v);
00091
00092 static const SpriteID _aircraft_sprite[] = {
00093 0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD,
00094 0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5,
00095 0x0EAD, 0x0EE5, 0x0F05, 0x0F0D,
00096 0x0F15, 0x0F1D, 0x0F25, 0x0F2D,
00097 0x0EED, 0x0EF5, 0x0EFD, 0x0F35,
00098 0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5,
00099 0x0EBD, 0x0EC5
00100 };
00101
00103 enum HelicopterRotorStates {
00104 HRS_ROTOR_STOPPED,
00105 HRS_ROTOR_MOVING_1,
00106 HRS_ROTOR_MOVING_2,
00107 HRS_ROTOR_MOVING_3,
00108 };
00109
00117 static StationID FindNearestHangar(const Aircraft *v)
00118 {
00119 const Station *st;
00120 uint best = 0;
00121 StationID index = INVALID_STATION;
00122 TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos);
00123 const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
00124
00125 FOR_ALL_STATIONS(st) {
00126 if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00127
00128 const AirportFTAClass *afc = st->airport.GetFTA();
00129 if (!st->airport.HasHangar() || (
00130
00131 (afc->flags & AirportFTAClass::SHORT_STRIP) &&
00132 (avi->subtype & AIR_FAST) &&
00133 !_cheats.no_jetcrash.value)) {
00134 continue;
00135 }
00136
00137
00138 uint distance = DistanceSquare(vtile, st->airport.tile);
00139 if (distance < best || index == INVALID_STATION) {
00140 best = distance;
00141 index = st->index;
00142 }
00143 }
00144 return index;
00145 }
00146
00147 SpriteID Aircraft::GetImage(Direction direction) const
00148 {
00149 uint8 spritenum = this->spritenum;
00150
00151 if (is_custom_sprite(spritenum)) {
00152 SpriteID sprite = GetCustomVehicleSprite(this, direction);
00153 if (sprite != 0) return sprite;
00154
00155 spritenum = Engine::Get(this->engine_type)->original_image_index;
00156 }
00157
00158 return direction + _aircraft_sprite[spritenum];
00159 }
00160
00161 SpriteID GetRotorImage(const Aircraft *v)
00162 {
00163 assert(v->subtype == AIR_HELICOPTER);
00164
00165 const Aircraft *w = v->Next()->Next();
00166 if (is_custom_sprite(v->spritenum)) {
00167 SpriteID sprite = GetCustomRotorSprite(v, false);
00168 if (sprite != 0) return sprite;
00169 }
00170
00171
00172 return SPR_ROTOR_STOPPED + w->state;
00173 }
00174
00175 static SpriteID GetAircraftIcon(EngineID engine)
00176 {
00177 const Engine *e = Engine::Get(engine);
00178 uint8 spritenum = e->u.air.image_index;
00179
00180 if (is_custom_sprite(spritenum)) {
00181 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00182 if (sprite != 0) return sprite;
00183
00184 spritenum = e->original_image_index;
00185 }
00186
00187 return DIR_W + _aircraft_sprite[spritenum];
00188 }
00189
00190 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal)
00191 {
00192 SpriteID sprite = GetAircraftIcon(engine);
00193 const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00194 preferred_x = Clamp(preferred_x, left - real_sprite->x_offs, right - real_sprite->width - real_sprite->x_offs);
00195 DrawSprite(sprite, pal, preferred_x, y);
00196
00197 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00198 SpriteID rotor_sprite = GetCustomRotorIcon(engine);
00199 if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00200 DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00201 }
00202 }
00203
00210 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
00211 {
00212 const Sprite *spr = GetSprite(GetAircraftIcon(engine), ST_NORMAL);
00213
00214 width = spr->width;
00215 height = spr->height;
00216 }
00217
00227 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
00228 {
00229 const AircraftVehicleInfo *avi = &e->u.air;
00230 const Station *st = Station::GetByTile(tile);
00231
00232
00233 if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
00234
00235
00236 tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
00237
00238 if (flags & DC_EXEC) {
00239 Aircraft *v = new Aircraft();
00240 Aircraft *u = new Aircraft();
00241 *ret = v;
00242
00243 v->direction = DIR_SE;
00244
00245 v->owner = u->owner = _current_company;
00246
00247 v->tile = tile;
00248
00249 uint x = TileX(tile) * TILE_SIZE + 5;
00250 uint y = TileY(tile) * TILE_SIZE + 3;
00251
00252 v->x_pos = u->x_pos = x;
00253 v->y_pos = u->y_pos = y;
00254
00255 u->z_pos = GetSlopeZ(x, y);
00256 v->z_pos = u->z_pos + 1;
00257
00258 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00259 u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00260
00261 v->spritenum = avi->image_index;
00262
00263 v->cargo_cap = avi->passenger_capacity;
00264 u->cargo_cap = avi->mail_capacity;
00265
00266 v->cargo_type = e->GetDefaultCargoType();
00267 u->cargo_type = CT_MAIL;
00268
00269 v->name = NULL;
00270 v->last_station_visited = 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 uint32 cargo_mask = 0;
00556 for (Aircraft *u = v; u != NULL; u = u->Next()) {
00557 if (u->cargo_type != INVALID_CARGO && u->cargo_cap > 0) SetBit(cargo_mask, u->cargo_type);
00558 }
00559 v->vcache.cached_cargo_mask = cargo_mask;
00560 }
00561
00562
00566 enum AircraftSpeedLimits {
00567 SPEED_LIMIT_TAXI = 50,
00568 SPEED_LIMIT_APPROACH = 230,
00569 SPEED_LIMIT_BROKEN = 320,
00570 SPEED_LIMIT_HOLD = 425,
00571 SPEED_LIMIT_NONE = 0xFFFF
00572 };
00573
00581 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00582 {
00583 uint spd = v->acceleration * 16;
00584 byte t;
00585
00586
00587
00588 speed_limit *= _settings_game.vehicle.plane_speed;
00589
00590 if (v->vcache.cached_max_speed < speed_limit) {
00591 if (v->cur_speed < speed_limit) hard_limit = false;
00592 speed_limit = v->vcache.cached_max_speed;
00593 }
00594
00595 v->subspeed = (t = v->subspeed) + (byte)spd;
00596
00597
00598
00599
00600
00601
00602
00603 if (!hard_limit && v->cur_speed > speed_limit) {
00604 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00605 }
00606
00607 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00608
00609
00610 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00611
00612
00613 if (spd != v->cur_speed) {
00614 v->cur_speed = spd;
00615 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00616 }
00617
00618
00619 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00620
00621
00622 spd = v->GetOldAdvanceSpeed(spd);
00623
00624 spd += v->progress;
00625 v->progress = (byte)spd;
00626 return spd >> 8;
00627 }
00628
00636 byte GetAircraftFlyingAltitude(const Aircraft *v)
00637 {
00638 if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00639
00640
00641
00642
00643 byte base_altitude = PLANE_HOLDING_ALTITUDE;
00644
00645
00646
00647
00648 switch (v->direction) {
00649 case DIR_N:
00650 case DIR_NE:
00651 case DIR_E:
00652 case DIR_SE:
00653 base_altitude += 10;
00654 break;
00655
00656 default: break;
00657 }
00658
00659
00660 base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00661
00662 return base_altitude;
00663 }
00664
00679 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00680 {
00681 assert(v != NULL);
00682 assert(apc != NULL);
00683
00684
00685
00686
00687 TileIndex tile = 0;
00688
00689 const Station *st = Station::GetIfValid(v->targetairport);
00690 if (st != NULL) {
00691
00692 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00693 }
00694
00695 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00696 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00697
00698 DiagDirection dir;
00699 if (abs(delta_y) < abs(delta_x)) {
00700
00701 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00702 } else {
00703
00704 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00705 }
00706 dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00707 return apc->entry_points[dir];
00708 }
00709
00710
00711 static void MaybeCrashAirplane(Aircraft *v);
00712
00720 static bool AircraftController(Aircraft *v)
00721 {
00722 int count;
00723
00724
00725 const Station *st = Station::GetIfValid(v->targetairport);
00726
00727 TileIndex tile = INVALID_TILE;
00728 Direction rotation = DIR_N;
00729 uint size_x = 1, size_y = 1;
00730 if (st != NULL) {
00731 if (st->airport.tile != INVALID_TILE) {
00732 tile = st->airport.tile;
00733 rotation = st->airport.rotation;
00734 size_x = st->airport.w;
00735 size_y = st->airport.h;
00736 } else {
00737 tile = st->xy;
00738 }
00739 }
00740
00741 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00742
00743
00744 if (st == NULL || st->airport.tile == INVALID_TILE) {
00745
00746 if (v->pos >= afc->nofelements) {
00747 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00748 } else if (v->targetairport != v->current_order.GetDestination()) {
00749
00750 v->state = FLYING;
00751 UpdateAircraftCache(v);
00752 AircraftNextAirportPos_and_Order(v);
00753
00754 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00755 return false;
00756 }
00757 }
00758
00759
00760 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00761
00762 int x = TileX(tile) * TILE_SIZE;
00763 int y = TileY(tile) * TILE_SIZE;
00764
00765
00766 if (amd.flag & AMED_HELI_RAISE) {
00767 Aircraft *u = v->Next()->Next();
00768
00769
00770 if (u->cur_speed > 32) {
00771 v->cur_speed = 0;
00772 if (--u->cur_speed == 32) {
00773 if (!PlayVehicleSound(v, VSE_START)) {
00774 SndPlayVehicleFx(SND_18_HELICOPTER, v);
00775 }
00776 }
00777 } else {
00778 u->cur_speed = 32;
00779 count = UpdateAircraftSpeed(v);
00780 if (count > 0) {
00781 v->tile = 0;
00782 byte z_dest = GetAircraftFlyingAltitude(v);
00783
00784
00785 if (v->z_pos >= z_dest) {
00786 v->cur_speed = 0;
00787 return true;
00788 }
00789 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00790 }
00791 }
00792 return false;
00793 }
00794
00795
00796 if (amd.flag & AMED_HELI_LOWER) {
00797 if (st == NULL) {
00798
00799
00800
00801 v->state = FLYING;
00802 UpdateAircraftCache(v);
00803 AircraftNextAirportPos_and_Order(v);
00804 return false;
00805 }
00806
00807
00808 v->tile = tile;
00809
00810
00811 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00812
00813 if (z == v->z_pos) {
00814 Vehicle *u = v->Next()->Next();
00815
00816
00817 if (u->cur_speed >= 80) return true;
00818 u->cur_speed += 4;
00819 } else {
00820 count = UpdateAircraftSpeed(v);
00821 if (count > 0) {
00822 if (v->z_pos > z) {
00823 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00824 } else {
00825 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00826 }
00827 }
00828 }
00829 return false;
00830 }
00831
00832
00833 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
00834
00835
00836 if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00837
00838
00839 if (dist == 0) {
00840
00841 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00842
00843
00844 if (dirdiff == DIRDIFF_SAME) {
00845 v->cur_speed = 0;
00846 return true;
00847 }
00848
00849 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00850
00851 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00852 v->cur_speed >>= 1;
00853
00854 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00855 return false;
00856 }
00857
00858 if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00859 MaybeCrashAirplane(v);
00860 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00861 }
00862
00863 uint speed_limit = SPEED_LIMIT_TAXI;
00864 bool hard_limit = true;
00865
00866 if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00867 if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00868 if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00869 if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00870
00871 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00872 if (count == 0) return false;
00873
00874 if (v->turn_counter != 0) v->turn_counter--;
00875
00876 do {
00877
00878 GetNewVehiclePosResult gp;
00879
00880 if (dist < 4 || (amd.flag & AMED_LAND)) {
00881
00882 gp.x = (v->x_pos != (x + amd.x)) ?
00883 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00884 v->x_pos;
00885 gp.y = (v->y_pos != (y + amd.y)) ?
00886 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00887 v->y_pos;
00888
00889
00890 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00891
00892 } else {
00893
00894
00895 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00896 if (newdir != v->direction) {
00897 if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00898 if (v->turn_counter == 0 || newdir == v->last_direction) {
00899 if (newdir == v->last_direction) {
00900 v->number_consecutive_turns = 0;
00901 } else {
00902 v->number_consecutive_turns++;
00903 }
00904 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00905 v->last_direction = v->direction;
00906 v->direction = newdir;
00907 }
00908
00909
00910 gp = GetNewVehiclePos(v);
00911 } else {
00912 v->cur_speed >>= 1;
00913 v->direction = newdir;
00914
00915
00916
00917
00918
00919
00920 gp.x = v->x_pos;
00921 gp.y = v->y_pos;
00922 gp.new_tile = gp.old_tile = v->tile;
00923 }
00924 } else {
00925 v->number_consecutive_turns = 0;
00926
00927 gp = GetNewVehiclePos(v);
00928 }
00929 }
00930
00931 v->tile = gp.new_tile;
00932
00933 if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00934
00935
00936 uint z = v->z_pos;
00937
00938 if (amd.flag & AMED_TAKEOFF) {
00939 z = min(z + 2, GetAircraftFlyingAltitude(v));
00940 }
00941
00942
00943 if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00944
00945 if (amd.flag & AMED_LAND) {
00946 if (st->airport.tile == INVALID_TILE) {
00947
00948 v->state = FLYING;
00949 UpdateAircraftCache(v);
00950 AircraftNextAirportPos_and_Order(v);
00951
00952 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00953 continue;
00954 }
00955
00956 uint curz = GetSlopeZ(x + amd.x, y + amd.y) + 1;
00957
00958
00959 assert(curz <= z);
00960 int t = max(1U, dist - 4);
00961 int delta = z - curz;
00962
00963
00964 if (delta >= t) {
00965 z -= CeilDiv(z - curz, t);
00966 }
00967 if (z < curz) z = curz;
00968 }
00969
00970
00971 if (amd.flag & AMED_BRAKE) {
00972 uint curz = GetSlopeZ(x, y) + 1;
00973
00974 if (z > curz) {
00975 z--;
00976 } else if (z < curz) {
00977 z++;
00978 }
00979
00980 }
00981
00982 SetAircraftPosition(v, gp.x, gp.y, z);
00983 } while (--count != 0);
00984 return false;
00985 }
00986
00991 static bool HandleCrashedAircraft(Aircraft *v)
00992 {
00993 v->crashed_counter += 3;
00994
00995 Station *st = GetTargetAirportIfValid(v);
00996
00997
00998 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
00999 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01000 v->z_pos -= 1;
01001 if (v->z_pos == z) {
01002 v->crashed_counter = 500;
01003 v->z_pos++;
01004 }
01005 }
01006
01007 if (v->crashed_counter < 650) {
01008 uint32 r;
01009 if (Chance16R(1, 32, r)) {
01010 static const DirDiff delta[] = {
01011 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01012 };
01013
01014 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01015 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01016 r = Random();
01017 CreateEffectVehicleRel(v,
01018 GB(r, 0, 4) - 4,
01019 GB(r, 4, 4) - 4,
01020 GB(r, 8, 4),
01021 EV_EXPLOSION_SMALL);
01022 }
01023 } else if (v->crashed_counter >= 10000) {
01024
01025
01026
01027
01028
01029 if (st != NULL) {
01030 CLRBITS(st->airport.flags, RUNWAY_IN_block);
01031 CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block);
01032 CLRBITS(st->airport.flags, RUNWAY_IN2_block);
01033 }
01034
01035 delete v;
01036
01037 return false;
01038 }
01039
01040 return true;
01041 }
01042
01043
01044 static void HandleAircraftSmoke(Aircraft *v)
01045 {
01046 static const struct {
01047 int8 x;
01048 int8 y;
01049 } smoke_pos[] = {
01050 { 5, 5 },
01051 { 6, 0 },
01052 { 5, -5 },
01053 { 0, -6 },
01054 { -5, -5 },
01055 { -6, 0 },
01056 { -5, 5 },
01057 { 0, 6 }
01058 };
01059
01060 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01061
01062 if (v->cur_speed < 10) {
01063 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01064 v->breakdown_ctr = 0;
01065 return;
01066 }
01067
01068 if ((v->tick_counter & 0x1F) == 0) {
01069 CreateEffectVehicleRel(v,
01070 smoke_pos[v->direction].x,
01071 smoke_pos[v->direction].y,
01072 2,
01073 EV_BREAKDOWN_SMOKE_AIRCRAFT
01074 );
01075 }
01076 }
01077
01078 void HandleMissingAircraftOrders(Aircraft *v)
01079 {
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095 const Station *st = GetTargetAirportIfValid(v);
01096 if (st == NULL) {
01097 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01098 CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01099 cur_company.Restore();
01100
01101 if (ret.Failed()) CrashAirplane(v);
01102 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01103 v->current_order.Free();
01104 }
01105 }
01106
01107
01108 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01109 {
01110
01111 if (this->state == FLYING) {
01112 AircraftNextAirportPos_and_Order(this);
01113 }
01114
01115
01116 return 0;
01117 }
01118
01119 void Aircraft::MarkDirty()
01120 {
01121 this->UpdateViewport(false, false);
01122 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01123 }
01124
01125
01126 uint Aircraft::Crash(bool flooded)
01127 {
01128 uint pass = Vehicle::Crash(flooded) + 2;
01129 this->crashed_counter = flooded ? 9000 : 0;
01130
01131 return pass;
01132 }
01133
01138 static void CrashAirplane(Aircraft *v)
01139 {
01140 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01141
01142 uint pass = v->Crash();
01143 SetDParam(0, pass);
01144
01145 v->cargo.Truncate(0);
01146 v->Next()->cargo.Truncate(0);
01147 const Station *st = GetTargetAirportIfValid(v);
01148 StringID newsitem;
01149 if (st == NULL) {
01150 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01151 } else {
01152 SetDParam(1, st->index);
01153 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01154 }
01155
01156 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, st == NULL ? AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : AIEventVehicleCrashed::CRASH_PLANE_LANDING));
01157
01158 AddVehicleNewsItem(newsitem,
01159 NS_ACCIDENT,
01160 v->index,
01161 st != NULL ? st->index : INVALID_STATION);
01162
01163 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01164 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01165 }
01166
01171 static void MaybeCrashAirplane(Aircraft *v)
01172 {
01173 if (_settings_game.vehicle.plane_crashes == 0) return;
01174
01175 Station *st = Station::Get(v->targetairport);
01176
01177
01178 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01179 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01180 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01181 !_cheats.no_jetcrash.value) {
01182 prob /= 20;
01183 } else {
01184 prob /= 1500;
01185 }
01186
01187 if (GB(Random(), 0, 22) > prob) return;
01188
01189
01190 for (CargoID i = 0; i < NUM_CARGO; i++) {
01191 st->goods[i].rating = 1;
01192 st->goods[i].cargo.Truncate(0);
01193 }
01194
01195 CrashAirplane(v);
01196 }
01197
01203 static void AircraftEntersTerminal(Aircraft *v)
01204 {
01205 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01206
01207 Station *st = Station::Get(v->targetairport);
01208
01209
01210 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01211 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01212 SetDParam(0, st->index);
01213
01214 AddVehicleNewsItem(
01215 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01216 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01217 v->index,
01218 st->index
01219 );
01220 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01221 }
01222
01223 v->BeginLoading(v->targetairport);
01224 }
01225
01230 static void AircraftLandAirplane(Aircraft *v)
01231 {
01232 v->UpdateDeltaXY(INVALID_DIR);
01233
01234 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01235 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01236 }
01237 }
01238
01239
01241 void AircraftNextAirportPos_and_Order(Aircraft *v)
01242 {
01243 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01244 v->targetairport = v->current_order.GetDestination();
01245 }
01246
01247 const Station *st = GetTargetAirportIfValid(v);
01248 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01249 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01250 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01251 }
01252
01253 void AircraftLeaveHangar(Aircraft *v)
01254 {
01255 v->cur_speed = 0;
01256 v->subspeed = 0;
01257 v->progress = 0;
01258 v->direction = DIR_SE;
01259 v->vehstatus &= ~VS_HIDDEN;
01260 {
01261 Vehicle *u = v->Next();
01262 u->vehstatus &= ~VS_HIDDEN;
01263
01264
01265 u = u->Next();
01266 if (u != NULL) {
01267 u->vehstatus &= ~VS_HIDDEN;
01268 u->cur_speed = 80;
01269 }
01270 }
01271
01272 VehicleServiceInDepot(v);
01273 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01274 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01275 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01276 }
01277
01281 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01282 {
01283 AircraftEntersTerminal(v);
01284 v->state = apc->layout[v->pos].heading;
01285 }
01286
01292 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01293 {
01294 VehicleEnterDepot(v);
01295 v->state = apc->layout[v->pos].heading;
01296 }
01297
01303 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01304 {
01305
01306 if (v->previous_pos != v->pos) {
01307 AircraftEventHandler_EnterHangar(v, apc);
01308 return;
01309 }
01310
01311
01312 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01313 v->current_order.Free();
01314 return;
01315 }
01316
01317 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01318 !v->current_order.IsType(OT_GOTO_DEPOT))
01319 return;
01320
01321
01322 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01323 VehicleEnterDepot(v);
01324 return;
01325 }
01326
01327
01328 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01329
01330
01331 if (v->current_order.GetDestination() == v->targetairport) {
01332
01333
01334 if (v->subtype == AIR_HELICOPTER) {
01335 if (!AirportFindFreeHelipad(v, apc)) return;
01336 } else {
01337 if (!AirportFindFreeTerminal(v, apc)) return;
01338 }
01339 } else {
01340
01341 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01342 }
01343 AircraftLeaveHangar(v);
01344 AirportMove(v, apc);
01345 }
01346
01348 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01349 {
01350
01351 if (v->previous_pos != v->pos) {
01352 AircraftEventHandler_EnterTerminal(v, apc);
01353
01354
01355 if (_settings_game.order.serviceathelipad) {
01356 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01357
01358 v->date_of_last_service = _date;
01359 v->breakdowns_since_last_service = 0;
01360 v->reliability = Engine::Get(v->engine_type)->reliability;
01361 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01362 }
01363 }
01364 return;
01365 }
01366
01367 if (v->current_order.IsType(OT_NOTHING)) return;
01368
01369
01370 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01371
01372
01373
01374
01375 bool go_to_hangar = false;
01376 switch (v->current_order.GetType()) {
01377 case OT_GOTO_STATION:
01378 break;
01379 case OT_GOTO_DEPOT:
01380 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01381 break;
01382 case OT_CONDITIONAL:
01383
01384
01385
01386 return;
01387 default:
01388 v->current_order.Free();
01389 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01390 }
01391
01392 if (go_to_hangar) {
01393 v->state = HANGAR;
01394 } else {
01395
01396 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01397 }
01398 AirportMove(v, apc);
01399 }
01400
01401 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01402 {
01403 error("OK, you shouldn't be here, check your Airport Scheme!");
01404 }
01405
01406 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01407 {
01408 PlayAircraftSound(v);
01409 v->state = STARTTAKEOFF;
01410 }
01411
01412 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01413 {
01414 v->state = ENDTAKEOFF;
01415 v->UpdateDeltaXY(INVALID_DIR);
01416 }
01417
01418 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01419 {
01420 v->state = FLYING;
01421
01422 AircraftNextAirportPos_and_Order(v);
01423 }
01424
01425 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01426 {
01427 v->state = FLYING;
01428 v->UpdateDeltaXY(INVALID_DIR);
01429
01430
01431 AircraftNextAirportPos_and_Order(v);
01432
01433
01434 if (v->NeedsAutomaticServicing()) {
01435 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01436 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01437 cur_company.Restore();
01438 }
01439 }
01440
01441 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01442 {
01443 Station *st = Station::Get(v->targetairport);
01444
01445
01446 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01447
01448
01449
01450 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01451 const AirportFTA *current = apc->layout[v->pos].next;
01452 while (current != NULL) {
01453 if (current->heading == landingtype) {
01454
01455
01456
01457 uint16 tcur_speed = v->cur_speed;
01458 uint16 tsubspeed = v->subspeed;
01459 if (!AirportHasBlock(v, current, apc)) {
01460 v->state = landingtype;
01461
01462
01463
01464 v->pos = current->next_position;
01465 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01466 return;
01467 }
01468 v->cur_speed = tcur_speed;
01469 v->subspeed = tsubspeed;
01470 }
01471 current = current->next;
01472 }
01473 }
01474 v->state = FLYING;
01475 v->pos = apc->layout[v->pos].next_position;
01476 }
01477
01478 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01479 {
01480 v->state = ENDLANDING;
01481 AircraftLandAirplane(v);
01482
01483
01484 if (v->NeedsAutomaticServicing()) {
01485 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01486 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01487 cur_company.Restore();
01488 }
01489 }
01490
01491 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01492 {
01493 v->state = HELIENDLANDING;
01494 v->UpdateDeltaXY(INVALID_DIR);
01495 }
01496
01497 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01498 {
01499
01500 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01501
01502
01503
01504
01505
01506 if (v->current_order.IsType(OT_GOTO_STATION)) {
01507 if (AirportFindFreeTerminal(v, apc)) return;
01508 }
01509 v->state = HANGAR;
01510
01511 }
01512
01513 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01514 {
01515
01516 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01517
01518
01519
01520
01521
01522
01523
01524
01525 if (v->current_order.IsType(OT_GOTO_STATION)) {
01526 if (AirportFindFreeHelipad(v, apc)) return;
01527 }
01528 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01529 }
01530
01536 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01538 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01539 AircraftEventHandler_General,
01540 AircraftEventHandler_InHangar,
01541 AircraftEventHandler_AtTerminal,
01542 AircraftEventHandler_AtTerminal,
01543 AircraftEventHandler_AtTerminal,
01544 AircraftEventHandler_AtTerminal,
01545 AircraftEventHandler_AtTerminal,
01546 AircraftEventHandler_AtTerminal,
01547 AircraftEventHandler_AtTerminal,
01548 AircraftEventHandler_AtTerminal,
01549 AircraftEventHandler_TakeOff,
01550 AircraftEventHandler_StartTakeOff,
01551 AircraftEventHandler_EndTakeOff,
01552 AircraftEventHandler_HeliTakeOff,
01553 AircraftEventHandler_Flying,
01554 AircraftEventHandler_Landing,
01555 AircraftEventHandler_EndLanding,
01556 AircraftEventHandler_HeliLanding,
01557 AircraftEventHandler_HeliEndLanding,
01558 AircraftEventHandler_AtTerminal,
01559 AircraftEventHandler_AtTerminal,
01560 AircraftEventHandler_AtTerminal,
01561 };
01562
01563 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01564 {
01565
01566 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01567 Station *st = Station::Get(v->targetairport);
01568
01569 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01570 }
01571 }
01572
01573 static void AirportGoToNextPosition(Aircraft *v)
01574 {
01575
01576 if (!AircraftController(v)) return;
01577
01578 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01579
01580 AirportClearBlock(v, apc);
01581 AirportMove(v, apc);
01582 }
01583
01584
01585 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01586 {
01587
01588 if (v->pos >= apc->nofelements) {
01589 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01590 assert(v->pos < apc->nofelements);
01591 }
01592
01593 const AirportFTA *current = &apc->layout[v->pos];
01594
01595 if (current->heading == v->state) {
01596 byte prev_pos = v->pos;
01597 byte prev_state = v->state;
01598 _aircraft_state_handlers[v->state](v, apc);
01599 if (v->state != FLYING) v->previous_pos = prev_pos;
01600 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01601 return true;
01602 }
01603
01604 v->previous_pos = v->pos;
01605
01606
01607 if (current->next == NULL) {
01608 if (AirportSetBlocks(v, current, apc)) {
01609 v->pos = current->next_position;
01610 UpdateAircraftCache(v);
01611 }
01612 return false;
01613 }
01614
01615
01616
01617 do {
01618 if (v->state == current->heading || current->heading == TO_ALL) {
01619 if (AirportSetBlocks(v, current, apc)) {
01620 v->pos = current->next_position;
01621 UpdateAircraftCache(v);
01622 }
01623 return false;
01624 }
01625 current = current->next;
01626 } while (current != NULL);
01627
01628 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01629 NOT_REACHED();
01630 }
01631
01633 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01634 {
01635 const AirportFTA *reference = &apc->layout[v->pos];
01636 const AirportFTA *next = &apc->layout[current_pos->next_position];
01637
01638
01639 if (apc->layout[current_pos->position].block != next->block) {
01640 const Station *st = Station::Get(v->targetairport);
01641 uint64 airport_flags = next->block;
01642
01643
01644 if (current_pos != reference && current_pos->block != NOTHING_block) {
01645 airport_flags |= current_pos->block;
01646 }
01647
01648 if (st->airport.flags & airport_flags) {
01649 v->cur_speed = 0;
01650 v->subspeed = 0;
01651 return true;
01652 }
01653 }
01654 return false;
01655 }
01656
01664 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01665 {
01666 const AirportFTA *next = &apc->layout[current_pos->next_position];
01667 const AirportFTA *reference = &apc->layout[v->pos];
01668
01669
01670 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01671 uint64 airport_flags = next->block;
01672
01673
01674 const AirportFTA *current = current_pos;
01675 if (current == reference) current = current->next;
01676 while (current != NULL) {
01677 if (current->heading == current_pos->heading && current->block != 0) {
01678 airport_flags |= current->block;
01679 break;
01680 }
01681 current = current->next;
01682 }
01683
01684
01685
01686 if (current_pos->block == next->block) airport_flags ^= next->block;
01687
01688 Station *st = Station::Get(v->targetairport);
01689 if (st->airport.flags & airport_flags) {
01690 v->cur_speed = 0;
01691 v->subspeed = 0;
01692 return false;
01693 }
01694
01695 if (next->block != NOTHING_block) {
01696 SETBITS(st->airport.flags, airport_flags);
01697 }
01698 }
01699 return true;
01700 }
01701
01706 struct MovementTerminalMapping {
01707 AirportMovementStates state;
01708 uint64 airport_flag;
01709 };
01710
01712 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01713 {TERM1, TERM1_block},
01714 {TERM2, TERM2_block},
01715 {TERM3, TERM3_block},
01716 {TERM4, TERM4_block},
01717 {TERM5, TERM5_block},
01718 {TERM6, TERM6_block},
01719 {TERM7, TERM7_block},
01720 {TERM8, TERM8_block},
01721 {HELIPAD1, HELIPAD1_block},
01722 {HELIPAD2, HELIPAD2_block},
01723 {HELIPAD3, HELIPAD3_block},
01724 };
01725
01733 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01734 {
01735 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01736 Station *st = Station::Get(v->targetairport);
01737 for (; i < last_terminal; i++) {
01738 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01739
01740 v->state = _airport_terminal_mapping[i].state;
01741 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01742 return true;
01743 }
01744 }
01745 return false;
01746 }
01747
01753 static uint GetNumTerminals(const AirportFTAClass *apc)
01754 {
01755 uint num = 0;
01756
01757 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01758
01759 return num;
01760 }
01761
01768 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01769 {
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780 if (apc->terminals[0] > 1) {
01781 const Station *st = Station::Get(v->targetairport);
01782 const AirportFTA *temp = apc->layout[v->pos].next;
01783
01784 while (temp != NULL) {
01785 if (temp->heading == 255) {
01786 if (!(st->airport.flags & temp->block)) {
01787
01788
01789 uint target_group = temp->next_position + 1;
01790
01791
01792
01793
01794 uint group_start = 0;
01795 for (uint i = 1; i < target_group; i++) {
01796 group_start += apc->terminals[i];
01797 }
01798
01799 uint group_end = group_start + apc->terminals[target_group];
01800 if (FreeTerminal(v, group_start, group_end)) return true;
01801 }
01802 } else {
01803
01804
01805 return false;
01806 }
01807 temp = temp->next;
01808 }
01809 }
01810
01811
01812 return FreeTerminal(v, 0, GetNumTerminals(apc));
01813 }
01814
01821 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01822 {
01823
01824 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01825
01826
01827
01828 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01829 }
01830
01831 static bool AircraftEventHandler(Aircraft *v, int loop)
01832 {
01833 v->tick_counter++;
01834
01835 if (v->vehstatus & VS_CRASHED) {
01836 return HandleCrashedAircraft(v);
01837 }
01838
01839 if (v->vehstatus & VS_STOPPED) return true;
01840
01841 v->HandleBreakdown();
01842
01843 HandleAircraftSmoke(v);
01844 ProcessOrders(v);
01845 v->HandleLoading(loop != 0);
01846
01847 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01848
01849 AirportGoToNextPosition(v);
01850
01851 return true;
01852 }
01853
01854 bool Aircraft::Tick()
01855 {
01856 if (!this->IsNormalAircraft()) return true;
01857
01858 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01859
01860 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01861
01862 this->current_order_time++;
01863
01864 for (uint i = 0; i != 2; i++) {
01865
01866 if (!AircraftEventHandler(this, i)) return false;
01867 }
01868
01869 return true;
01870 }
01871
01872
01879 Station *GetTargetAirportIfValid(const Aircraft *v)
01880 {
01881 assert(v->type == VEH_AIRCRAFT);
01882
01883 Station *st = Station::GetIfValid(v->targetairport);
01884 if (st == NULL) return NULL;
01885
01886 return st->airport.tile == INVALID_TILE ? NULL : st;
01887 }
01888
01893 void UpdateAirplanesOnNewStation(const Station *st)
01894 {
01895
01896 const AirportFTAClass *ap = st->airport.GetFTA();
01897 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01898
01899 Aircraft *v;
01900 FOR_ALL_AIRCRAFT(v) {
01901 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01902 assert(v->state == FLYING);
01903 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01904 UpdateAircraftCache(v);
01905 }
01906 }