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