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 "newgrf_engine.h"
00020 #include "newgrf_sound.h"
00021 #include "spritecache.h"
00022 #include "strings_func.h"
00023 #include "command_func.h"
00024 #include "window_func.h"
00025 #include "date_func.h"
00026 #include "vehicle_func.h"
00027 #include "sound_func.h"
00028 #include "cheat_type.h"
00029 #include "company_base.h"
00030 #include "ai/ai.hpp"
00031 #include "game/game.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, true);
00316
00317 VehicleUpdatePosition(v);
00318 VehicleUpdatePosition(u);
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 VehicleUpdatePosition(w);
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, WID_VV_START_STOP);
00386 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00387 v->current_order.MakeDummy();
00388 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
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 VehicleUpdatePositionAndViewport(u);
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 VehicleUpdatePosition(v);
00484 v->UpdateViewport(true, false);
00485 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
00486
00487 Aircraft *u = v->Next();
00488
00489 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00490 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00491 u->x_pos = x;
00492 u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
00493
00494 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00495 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
00496 u->cur_image = v->cur_image;
00497
00498 VehicleUpdatePositionAndViewport(u);
00499
00500 u = u->Next();
00501 if (u != NULL) {
00502 u->x_pos = x;
00503 u->y_pos = y;
00504 u->z_pos = z + ROTOR_Z_OFFSET;
00505
00506 VehicleUpdatePositionAndViewport(u);
00507 }
00508 }
00509
00514 void HandleAircraftEnterHangar(Aircraft *v)
00515 {
00516 v->subspeed = 0;
00517 v->progress = 0;
00518
00519 Aircraft *u = v->Next();
00520 u->vehstatus |= VS_HIDDEN;
00521 u = u->Next();
00522 if (u != NULL) {
00523 u->vehstatus |= VS_HIDDEN;
00524 u->cur_speed = 0;
00525 }
00526
00527 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00528 }
00529
00530 static void PlayAircraftSound(const Vehicle *v)
00531 {
00532 if (!PlayVehicleSound(v, VSE_START)) {
00533 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00534 }
00535 }
00536
00537
00544 void UpdateAircraftCache(Aircraft *v, bool update_range)
00545 {
00546 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00547 if (max_speed != 0) {
00548
00549 max_speed = (max_speed * 128) / 10;
00550
00551 v->vcache.cached_max_speed = max_speed;
00552 } else {
00553
00554 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00555 }
00556
00557
00558 v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00559 Aircraft *u = v->Next();
00560 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00561
00562
00563 if (update_range) {
00564 v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
00565
00566 v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
00567 }
00568 }
00569
00570
00574 enum AircraftSpeedLimits {
00575 SPEED_LIMIT_TAXI = 50,
00576 SPEED_LIMIT_APPROACH = 230,
00577 SPEED_LIMIT_BROKEN = 320,
00578 SPEED_LIMIT_HOLD = 425,
00579 SPEED_LIMIT_NONE = 0xFFFF,
00580 };
00581
00589 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00590 {
00591 uint spd = v->acceleration * 16;
00592 byte t;
00593
00594
00595
00596 speed_limit *= _settings_game.vehicle.plane_speed;
00597
00598 if (v->vcache.cached_max_speed < speed_limit) {
00599 if (v->cur_speed < speed_limit) hard_limit = false;
00600 speed_limit = v->vcache.cached_max_speed;
00601 }
00602
00603 v->subspeed = (t = v->subspeed) + (byte)spd;
00604
00605
00606
00607
00608
00609
00610
00611 if (!hard_limit && v->cur_speed > speed_limit) {
00612 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00613 }
00614
00615 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00616
00617
00618 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00619
00620
00621 if (spd != v->cur_speed) {
00622 v->cur_speed = spd;
00623 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00624 }
00625
00626
00627 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00628
00629
00630 spd = v->GetOldAdvanceSpeed(spd);
00631
00632 spd += v->progress;
00633 v->progress = (byte)spd;
00634 return spd >> 8;
00635 }
00636
00644 int GetAircraftFlyingAltitude(const Aircraft *v)
00645 {
00646 if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00647
00648
00649
00650
00651 int base_altitude = PLANE_HOLDING_ALTITUDE;
00652
00653
00654
00655
00656 switch (v->direction) {
00657 case DIR_N:
00658 case DIR_NE:
00659 case DIR_E:
00660 case DIR_SE:
00661 base_altitude += 10;
00662 break;
00663
00664 default: break;
00665 }
00666
00667
00668 base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00669
00670 return base_altitude;
00671 }
00672
00687 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00688 {
00689 assert(v != NULL);
00690 assert(apc != NULL);
00691
00692
00693
00694
00695 TileIndex tile = 0;
00696
00697 const Station *st = Station::GetIfValid(v->targetairport);
00698 if (st != NULL) {
00699
00700 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00701 }
00702
00703 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00704 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00705
00706 DiagDirection dir;
00707 if (abs(delta_y) < abs(delta_x)) {
00708
00709 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00710 } else {
00711
00712 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00713 }
00714 dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00715 return apc->entry_points[dir];
00716 }
00717
00718
00719 static void MaybeCrashAirplane(Aircraft *v);
00720
00728 static bool AircraftController(Aircraft *v)
00729 {
00730 int count;
00731
00732
00733 const Station *st = Station::GetIfValid(v->targetairport);
00734
00735 TileIndex tile = INVALID_TILE;
00736 Direction rotation = DIR_N;
00737 uint size_x = 1, size_y = 1;
00738 if (st != NULL) {
00739 if (st->airport.tile != INVALID_TILE) {
00740 tile = st->airport.tile;
00741 rotation = st->airport.rotation;
00742 size_x = st->airport.w;
00743 size_y = st->airport.h;
00744 } else {
00745 tile = st->xy;
00746 }
00747 }
00748
00749 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00750
00751
00752 if (st == NULL || st->airport.tile == INVALID_TILE) {
00753
00754 if (v->pos >= afc->nofelements) {
00755 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00756 } else if (v->targetairport != v->current_order.GetDestination()) {
00757
00758 v->state = FLYING;
00759 UpdateAircraftCache(v);
00760 AircraftNextAirportPos_and_Order(v);
00761
00762 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00763 return false;
00764 }
00765 }
00766
00767
00768 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00769
00770 int x = TileX(tile) * TILE_SIZE;
00771 int y = TileY(tile) * TILE_SIZE;
00772
00773
00774 if (amd.flag & AMED_HELI_RAISE) {
00775 Aircraft *u = v->Next()->Next();
00776
00777
00778 if (u->cur_speed > 32) {
00779 v->cur_speed = 0;
00780 if (--u->cur_speed == 32) {
00781 if (!PlayVehicleSound(v, VSE_START)) {
00782 SndPlayVehicleFx(SND_18_HELICOPTER, v);
00783 }
00784 }
00785 } else {
00786 u->cur_speed = 32;
00787 count = UpdateAircraftSpeed(v);
00788 if (count > 0) {
00789 v->tile = 0;
00790 int z_dest = GetAircraftFlyingAltitude(v);
00791
00792
00793 if (v->z_pos >= z_dest) {
00794 v->cur_speed = 0;
00795 return true;
00796 }
00797 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00798 }
00799 }
00800 return false;
00801 }
00802
00803
00804 if (amd.flag & AMED_HELI_LOWER) {
00805 if (st == NULL) {
00806
00807
00808
00809 v->state = FLYING;
00810 UpdateAircraftCache(v);
00811 AircraftNextAirportPos_and_Order(v);
00812 return false;
00813 }
00814
00815
00816 v->tile = tile;
00817
00818
00819 int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00820
00821 if (z == v->z_pos) {
00822 Vehicle *u = v->Next()->Next();
00823
00824
00825 if (u->cur_speed >= 80) return true;
00826 u->cur_speed += 4;
00827 } else {
00828 count = UpdateAircraftSpeed(v);
00829 if (count > 0) {
00830 if (v->z_pos > z) {
00831 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00832 } else {
00833 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00834 }
00835 }
00836 }
00837 return false;
00838 }
00839
00840
00841 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
00842
00843
00844 if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00845
00846
00847 if (dist == 0) {
00848
00849 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00850
00851
00852 if (dirdiff == DIRDIFF_SAME) {
00853 v->cur_speed = 0;
00854 return true;
00855 }
00856
00857 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00858
00859 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00860 v->cur_speed >>= 1;
00861
00862 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00863 return false;
00864 }
00865
00866 if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00867 MaybeCrashAirplane(v);
00868 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00869 }
00870
00871 uint speed_limit = SPEED_LIMIT_TAXI;
00872 bool hard_limit = true;
00873
00874 if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00875 if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00876 if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00877 if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00878
00879 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00880 if (count == 0) return false;
00881
00882 if (v->turn_counter != 0) v->turn_counter--;
00883
00884 do {
00885
00886 GetNewVehiclePosResult gp;
00887
00888 if (dist < 4 || (amd.flag & AMED_LAND)) {
00889
00890 gp.x = (v->x_pos != (x + amd.x)) ?
00891 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00892 v->x_pos;
00893 gp.y = (v->y_pos != (y + amd.y)) ?
00894 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00895 v->y_pos;
00896
00897
00898 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00899
00900 } else {
00901
00902
00903 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00904 if (newdir != v->direction) {
00905 if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00906 if (v->turn_counter == 0 || newdir == v->last_direction) {
00907 if (newdir == v->last_direction) {
00908 v->number_consecutive_turns = 0;
00909 } else {
00910 v->number_consecutive_turns++;
00911 }
00912 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00913 v->last_direction = v->direction;
00914 v->direction = newdir;
00915 }
00916
00917
00918 gp = GetNewVehiclePos(v);
00919 } else {
00920 v->cur_speed >>= 1;
00921 v->direction = newdir;
00922
00923
00924
00925
00926
00927
00928 gp.x = v->x_pos;
00929 gp.y = v->y_pos;
00930 gp.new_tile = gp.old_tile = v->tile;
00931 }
00932 } else {
00933 v->number_consecutive_turns = 0;
00934
00935 gp = GetNewVehiclePos(v);
00936 }
00937 }
00938
00939 v->tile = gp.new_tile;
00940
00941 if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00942
00943
00944 int z = v->z_pos;
00945
00946 if (amd.flag & AMED_TAKEOFF) {
00947 z = min(z + 2, GetAircraftFlyingAltitude(v));
00948 }
00949
00950
00951 if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00952
00953 if (amd.flag & AMED_LAND) {
00954 if (st->airport.tile == INVALID_TILE) {
00955
00956 v->state = FLYING;
00957 UpdateAircraftCache(v);
00958 AircraftNextAirportPos_and_Order(v);
00959
00960 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00961 continue;
00962 }
00963
00964 int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00965
00966
00967 assert(curz <= z);
00968 int t = max(1U, dist - 4);
00969 int delta = z - curz;
00970
00971
00972 if (delta >= t) {
00973 z -= CeilDiv(z - curz, t);
00974 }
00975 if (z < curz) z = curz;
00976 }
00977
00978
00979 if (amd.flag & AMED_BRAKE) {
00980 int curz = GetSlopePixelZ(x, y) + 1;
00981
00982 if (z > curz) {
00983 z--;
00984 } else if (z < curz) {
00985 z++;
00986 }
00987
00988 }
00989
00990 SetAircraftPosition(v, gp.x, gp.y, z);
00991 } while (--count != 0);
00992 return false;
00993 }
00994
00999 static bool HandleCrashedAircraft(Aircraft *v)
01000 {
01001 v->crashed_counter += 3;
01002
01003 Station *st = GetTargetAirportIfValid(v);
01004
01005
01006 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01007 int z = GetSlopePixelZ(v->x_pos, v->y_pos);
01008 v->z_pos -= 1;
01009 if (v->z_pos == z) {
01010 v->crashed_counter = 500;
01011 v->z_pos++;
01012 }
01013 }
01014
01015 if (v->crashed_counter < 650) {
01016 uint32 r;
01017 if (Chance16R(1, 32, r)) {
01018 static const DirDiff delta[] = {
01019 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01020 };
01021
01022 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01023 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01024 r = Random();
01025 CreateEffectVehicleRel(v,
01026 GB(r, 0, 4) - 4,
01027 GB(r, 4, 4) - 4,
01028 GB(r, 8, 4),
01029 EV_EXPLOSION_SMALL);
01030 }
01031 } else if (v->crashed_counter >= 10000) {
01032
01033
01034
01035
01036
01037 if (st != NULL) {
01038 CLRBITS(st->airport.flags, RUNWAY_IN_block);
01039 CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block);
01040 CLRBITS(st->airport.flags, RUNWAY_IN2_block);
01041 }
01042
01043 delete v;
01044
01045 return false;
01046 }
01047
01048 return true;
01049 }
01050
01051
01052 static void HandleAircraftSmoke(Aircraft *v)
01053 {
01054 static const struct {
01055 int8 x;
01056 int8 y;
01057 } smoke_pos[] = {
01058 { 5, 5 },
01059 { 6, 0 },
01060 { 5, -5 },
01061 { 0, -6 },
01062 { -5, -5 },
01063 { -6, 0 },
01064 { -5, 5 },
01065 { 0, 6 }
01066 };
01067
01068 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01069
01070 if (v->cur_speed < 10) {
01071 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01072 v->breakdown_ctr = 0;
01073 return;
01074 }
01075
01076 if ((v->tick_counter & 0x1F) == 0) {
01077 CreateEffectVehicleRel(v,
01078 smoke_pos[v->direction].x,
01079 smoke_pos[v->direction].y,
01080 2,
01081 EV_BREAKDOWN_SMOKE_AIRCRAFT
01082 );
01083 }
01084 }
01085
01086 void HandleMissingAircraftOrders(Aircraft *v)
01087 {
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103 const Station *st = GetTargetAirportIfValid(v);
01104 if (st == NULL) {
01105 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01106 CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01107 cur_company.Restore();
01108
01109 if (ret.Failed()) CrashAirplane(v);
01110 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01111 v->current_order.Free();
01112 }
01113 }
01114
01115
01116 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01117 {
01118
01119 if (this->state == FLYING) {
01120 AircraftNextAirportPos_and_Order(this);
01121 }
01122
01123
01124 return 0;
01125 }
01126
01127 void Aircraft::MarkDirty()
01128 {
01129 this->UpdateViewport(false, false);
01130 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01131 }
01132
01133
01134 uint Aircraft::Crash(bool flooded)
01135 {
01136 uint pass = Vehicle::Crash(flooded) + 2;
01137 this->crashed_counter = flooded ? 9000 : 0;
01138
01139 return pass;
01140 }
01141
01146 static void CrashAirplane(Aircraft *v)
01147 {
01148 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01149
01150 uint pass = v->Crash();
01151 SetDParam(0, pass);
01152
01153 v->cargo.Truncate(0);
01154 v->Next()->cargo.Truncate(0);
01155 const Station *st = GetTargetAirportIfValid(v);
01156 StringID newsitem;
01157 if (st == NULL) {
01158 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01159 } else {
01160 SetDParam(1, st->index);
01161 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01162 }
01163
01164 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01165 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01166
01167 AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
01168
01169 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01170 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01171 }
01172
01177 static void MaybeCrashAirplane(Aircraft *v)
01178 {
01179 if (_settings_game.vehicle.plane_crashes == 0) return;
01180
01181 Station *st = Station::Get(v->targetairport);
01182
01183
01184 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01185 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01186 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01187 !_cheats.no_jetcrash.value) {
01188 prob /= 20;
01189 } else {
01190 prob /= 1500;
01191 }
01192
01193 if (GB(Random(), 0, 22) > prob) return;
01194
01195
01196 for (CargoID i = 0; i < NUM_CARGO; i++) {
01197 st->goods[i].rating = 1;
01198 st->goods[i].cargo.Truncate(0);
01199 }
01200
01201 CrashAirplane(v);
01202 }
01203
01209 static void AircraftEntersTerminal(Aircraft *v)
01210 {
01211 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01212
01213 Station *st = Station::Get(v->targetairport);
01214 v->last_station_visited = v->targetairport;
01215
01216
01217 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01218 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01219 SetDParam(0, st->index);
01220
01221 AddVehicleNewsItem(
01222 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01223 (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
01224 v->index,
01225 st->index
01226 );
01227 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01228 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
01229 }
01230
01231 v->BeginLoading();
01232 }
01233
01238 static void AircraftLandAirplane(Aircraft *v)
01239 {
01240 v->UpdateDeltaXY(INVALID_DIR);
01241
01242 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01243 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01244 }
01245 }
01246
01247
01249 void AircraftNextAirportPos_and_Order(Aircraft *v)
01250 {
01251 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01252 v->targetairport = v->current_order.GetDestination();
01253 }
01254
01255 const Station *st = GetTargetAirportIfValid(v);
01256 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01257 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01258 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01259 }
01260
01269 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01270 {
01271 v->cur_speed = 0;
01272 v->subspeed = 0;
01273 v->progress = 0;
01274 v->direction = exit_dir;
01275 v->vehstatus &= ~VS_HIDDEN;
01276 {
01277 Vehicle *u = v->Next();
01278 u->vehstatus &= ~VS_HIDDEN;
01279
01280
01281 u = u->Next();
01282 if (u != NULL) {
01283 u->vehstatus &= ~VS_HIDDEN;
01284 u->cur_speed = 80;
01285 }
01286 }
01287
01288 VehicleServiceInDepot(v);
01289 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01290 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01291 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01292 }
01293
01297 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01298 {
01299 AircraftEntersTerminal(v);
01300 v->state = apc->layout[v->pos].heading;
01301 }
01302
01308 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01309 {
01310 VehicleEnterDepot(v);
01311 v->state = apc->layout[v->pos].heading;
01312 }
01313
01319 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01320 {
01321
01322 if (v->previous_pos != v->pos) {
01323 AircraftEventHandler_EnterHangar(v, apc);
01324 return;
01325 }
01326
01327
01328 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01329 v->current_order.Free();
01330 return;
01331 }
01332
01333 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01334 !v->current_order.IsType(OT_GOTO_DEPOT))
01335 return;
01336
01337
01338 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01339 VehicleEnterDepot(v);
01340 return;
01341 }
01342
01343
01344 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01345
01346
01347 if (v->current_order.GetDestination() == v->targetairport) {
01348
01349
01350 if (v->subtype == AIR_HELICOPTER) {
01351 if (!AirportFindFreeHelipad(v, apc)) return;
01352 } else {
01353 if (!AirportFindFreeTerminal(v, apc)) return;
01354 }
01355 } else {
01356
01357 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01358 }
01359 const Station *st = Station::GetByTile(v->tile);
01360 AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01361 AirportMove(v, apc);
01362 }
01363
01365 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01366 {
01367
01368 if (v->previous_pos != v->pos) {
01369 AircraftEventHandler_EnterTerminal(v, apc);
01370
01371
01372 if (_settings_game.order.serviceathelipad) {
01373 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01374
01375 v->date_of_last_service = _date;
01376 v->breakdowns_since_last_service = 0;
01377 v->reliability = v->GetEngine()->reliability;
01378 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01379 }
01380 }
01381 return;
01382 }
01383
01384 if (v->current_order.IsType(OT_NOTHING)) return;
01385
01386
01387 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01388
01389
01390
01391
01392 bool go_to_hangar = false;
01393 switch (v->current_order.GetType()) {
01394 case OT_GOTO_STATION:
01395 break;
01396 case OT_GOTO_DEPOT:
01397 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01398 break;
01399 case OT_CONDITIONAL:
01400
01401
01402
01403 return;
01404 default:
01405 v->current_order.Free();
01406 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01407 }
01408
01409 if (go_to_hangar) {
01410 v->state = HANGAR;
01411 } else {
01412
01413 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01414 }
01415 AirportMove(v, apc);
01416 }
01417
01418 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01419 {
01420 error("OK, you shouldn't be here, check your Airport Scheme!");
01421 }
01422
01423 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01424 {
01425 PlayAircraftSound(v);
01426 v->state = STARTTAKEOFF;
01427 }
01428
01429 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01430 {
01431 v->state = ENDTAKEOFF;
01432 v->UpdateDeltaXY(INVALID_DIR);
01433 }
01434
01435 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01436 {
01437 v->state = FLYING;
01438
01439 AircraftNextAirportPos_and_Order(v);
01440 }
01441
01442 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01443 {
01444 v->state = FLYING;
01445 v->UpdateDeltaXY(INVALID_DIR);
01446
01447
01448 AircraftNextAirportPos_and_Order(v);
01449
01450
01451 if (v->NeedsAutomaticServicing()) {
01452 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01453 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01454 cur_company.Restore();
01455 }
01456 }
01457
01458 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01459 {
01460 Station *st = Station::Get(v->targetairport);
01461
01462
01463 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
01464
01465
01466
01467 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01468 const AirportFTA *current = apc->layout[v->pos].next;
01469 while (current != NULL) {
01470 if (current->heading == landingtype) {
01471
01472
01473
01474 uint16 tcur_speed = v->cur_speed;
01475 uint16 tsubspeed = v->subspeed;
01476 if (!AirportHasBlock(v, current, apc)) {
01477 v->state = landingtype;
01478
01479
01480
01481 v->pos = current->next_position;
01482 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01483 return;
01484 }
01485 v->cur_speed = tcur_speed;
01486 v->subspeed = tsubspeed;
01487 }
01488 current = current->next;
01489 }
01490 }
01491 v->state = FLYING;
01492 v->pos = apc->layout[v->pos].next_position;
01493 }
01494
01495 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01496 {
01497 v->state = ENDLANDING;
01498 AircraftLandAirplane(v);
01499
01500
01501 if (v->NeedsAutomaticServicing()) {
01502 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01503 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01504 cur_company.Restore();
01505 }
01506 }
01507
01508 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01509 {
01510 v->state = HELIENDLANDING;
01511 v->UpdateDeltaXY(INVALID_DIR);
01512 }
01513
01514 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01515 {
01516
01517 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01518
01519
01520
01521
01522
01523 if (v->current_order.IsType(OT_GOTO_STATION)) {
01524 if (AirportFindFreeTerminal(v, apc)) return;
01525 }
01526 v->state = HANGAR;
01527
01528 }
01529
01530 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01531 {
01532
01533 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01534
01535
01536
01537
01538
01539
01540
01541
01542 if (v->current_order.IsType(OT_GOTO_STATION)) {
01543 if (AirportFindFreeHelipad(v, apc)) return;
01544 }
01545 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01546 }
01547
01553 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01555 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01556 AircraftEventHandler_General,
01557 AircraftEventHandler_InHangar,
01558 AircraftEventHandler_AtTerminal,
01559 AircraftEventHandler_AtTerminal,
01560 AircraftEventHandler_AtTerminal,
01561 AircraftEventHandler_AtTerminal,
01562 AircraftEventHandler_AtTerminal,
01563 AircraftEventHandler_AtTerminal,
01564 AircraftEventHandler_AtTerminal,
01565 AircraftEventHandler_AtTerminal,
01566 AircraftEventHandler_TakeOff,
01567 AircraftEventHandler_StartTakeOff,
01568 AircraftEventHandler_EndTakeOff,
01569 AircraftEventHandler_HeliTakeOff,
01570 AircraftEventHandler_Flying,
01571 AircraftEventHandler_Landing,
01572 AircraftEventHandler_EndLanding,
01573 AircraftEventHandler_HeliLanding,
01574 AircraftEventHandler_HeliEndLanding,
01575 AircraftEventHandler_AtTerminal,
01576 AircraftEventHandler_AtTerminal,
01577 AircraftEventHandler_AtTerminal,
01578 };
01579
01580 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01581 {
01582
01583 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01584 Station *st = Station::Get(v->targetairport);
01585
01586 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01587 }
01588 }
01589
01590 static void AirportGoToNextPosition(Aircraft *v)
01591 {
01592
01593 if (!AircraftController(v)) return;
01594
01595 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01596
01597 AirportClearBlock(v, apc);
01598 AirportMove(v, apc);
01599 }
01600
01601
01602 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01603 {
01604
01605 if (v->pos >= apc->nofelements) {
01606 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01607 assert(v->pos < apc->nofelements);
01608 }
01609
01610 const AirportFTA *current = &apc->layout[v->pos];
01611
01612 if (current->heading == v->state) {
01613 byte prev_pos = v->pos;
01614 byte prev_state = v->state;
01615 _aircraft_state_handlers[v->state](v, apc);
01616 if (v->state != FLYING) v->previous_pos = prev_pos;
01617 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01618 return true;
01619 }
01620
01621 v->previous_pos = v->pos;
01622
01623
01624 if (current->next == NULL) {
01625 if (AirportSetBlocks(v, current, apc)) {
01626 v->pos = current->next_position;
01627 UpdateAircraftCache(v);
01628 }
01629 return false;
01630 }
01631
01632
01633
01634 do {
01635 if (v->state == current->heading || current->heading == TO_ALL) {
01636 if (AirportSetBlocks(v, current, apc)) {
01637 v->pos = current->next_position;
01638 UpdateAircraftCache(v);
01639 }
01640 return false;
01641 }
01642 current = current->next;
01643 } while (current != NULL);
01644
01645 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01646 NOT_REACHED();
01647 }
01648
01650 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01651 {
01652 const AirportFTA *reference = &apc->layout[v->pos];
01653 const AirportFTA *next = &apc->layout[current_pos->next_position];
01654
01655
01656 if (apc->layout[current_pos->position].block != next->block) {
01657 const Station *st = Station::Get(v->targetairport);
01658 uint64 airport_flags = next->block;
01659
01660
01661 if (current_pos != reference && current_pos->block != NOTHING_block) {
01662 airport_flags |= current_pos->block;
01663 }
01664
01665 if (st->airport.flags & airport_flags) {
01666 v->cur_speed = 0;
01667 v->subspeed = 0;
01668 return true;
01669 }
01670 }
01671 return false;
01672 }
01673
01681 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01682 {
01683 const AirportFTA *next = &apc->layout[current_pos->next_position];
01684 const AirportFTA *reference = &apc->layout[v->pos];
01685
01686
01687 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01688 uint64 airport_flags = next->block;
01689
01690
01691 const AirportFTA *current = current_pos;
01692 if (current == reference) current = current->next;
01693 while (current != NULL) {
01694 if (current->heading == current_pos->heading && current->block != 0) {
01695 airport_flags |= current->block;
01696 break;
01697 }
01698 current = current->next;
01699 }
01700
01701
01702
01703 if (current_pos->block == next->block) airport_flags ^= next->block;
01704
01705 Station *st = Station::Get(v->targetairport);
01706 if (st->airport.flags & airport_flags) {
01707 v->cur_speed = 0;
01708 v->subspeed = 0;
01709 return false;
01710 }
01711
01712 if (next->block != NOTHING_block) {
01713 SETBITS(st->airport.flags, airport_flags);
01714 }
01715 }
01716 return true;
01717 }
01718
01723 struct MovementTerminalMapping {
01724 AirportMovementStates state;
01725 uint64 airport_flag;
01726 };
01727
01729 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01730 {TERM1, TERM1_block},
01731 {TERM2, TERM2_block},
01732 {TERM3, TERM3_block},
01733 {TERM4, TERM4_block},
01734 {TERM5, TERM5_block},
01735 {TERM6, TERM6_block},
01736 {TERM7, TERM7_block},
01737 {TERM8, TERM8_block},
01738 {HELIPAD1, HELIPAD1_block},
01739 {HELIPAD2, HELIPAD2_block},
01740 {HELIPAD3, HELIPAD3_block},
01741 };
01742
01750 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01751 {
01752 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01753 Station *st = Station::Get(v->targetairport);
01754 for (; i < last_terminal; i++) {
01755 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01756
01757 v->state = _airport_terminal_mapping[i].state;
01758 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01759 return true;
01760 }
01761 }
01762 return false;
01763 }
01764
01770 static uint GetNumTerminals(const AirportFTAClass *apc)
01771 {
01772 uint num = 0;
01773
01774 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01775
01776 return num;
01777 }
01778
01785 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01786 {
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797 if (apc->terminals[0] > 1) {
01798 const Station *st = Station::Get(v->targetairport);
01799 const AirportFTA *temp = apc->layout[v->pos].next;
01800
01801 while (temp != NULL) {
01802 if (temp->heading == 255) {
01803 if (!(st->airport.flags & temp->block)) {
01804
01805
01806 uint target_group = temp->next_position + 1;
01807
01808
01809
01810
01811 uint group_start = 0;
01812 for (uint i = 1; i < target_group; i++) {
01813 group_start += apc->terminals[i];
01814 }
01815
01816 uint group_end = group_start + apc->terminals[target_group];
01817 if (FreeTerminal(v, group_start, group_end)) return true;
01818 }
01819 } else {
01820
01821
01822 return false;
01823 }
01824 temp = temp->next;
01825 }
01826 }
01827
01828
01829 return FreeTerminal(v, 0, GetNumTerminals(apc));
01830 }
01831
01838 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01839 {
01840
01841 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01842
01843
01844
01845 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01846 }
01847
01853 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01854 {
01855 if (too_far) {
01856 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01857 SetBit(v->flags, VAF_DEST_TOO_FAR);
01858 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01859 AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01860 if (v->owner == _local_company) {
01861
01862 SetDParam(0, v->index);
01863 AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
01864 }
01865 }
01866 return;
01867 }
01868
01869 if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01870
01871 ClrBit(v->flags, VAF_DEST_TOO_FAR);
01872 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01873 DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01874 }
01875 }
01876
01877 static bool AircraftEventHandler(Aircraft *v, int loop)
01878 {
01879 v->tick_counter++;
01880
01881 if (v->vehstatus & VS_CRASHED) {
01882 return HandleCrashedAircraft(v);
01883 }
01884
01885 if (v->vehstatus & VS_STOPPED) return true;
01886
01887 v->HandleBreakdown();
01888
01889 HandleAircraftSmoke(v);
01890 ProcessOrders(v);
01891 v->HandleLoading(loop != 0);
01892
01893 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01894
01895 if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
01896
01897 AircraftHandleDestTooFar(v, false);
01898 } else if (v->acache.cached_max_range_sqr != 0) {
01899
01900
01901 Station *cur_st = Station::GetIfValid(v->targetairport);
01902 Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01903
01904 if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01905 uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01906 AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01907 }
01908 }
01909
01910 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01911
01912 return true;
01913 }
01914
01915 bool Aircraft::Tick()
01916 {
01917 if (!this->IsNormalAircraft()) return true;
01918
01919 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01920
01921 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01922
01923 this->current_order_time++;
01924
01925 for (uint i = 0; i != 2; i++) {
01926
01927 if (!AircraftEventHandler(this, i)) return false;
01928 }
01929
01930 return true;
01931 }
01932
01933
01940 Station *GetTargetAirportIfValid(const Aircraft *v)
01941 {
01942 assert(v->type == VEH_AIRCRAFT);
01943
01944 Station *st = Station::GetIfValid(v->targetairport);
01945 if (st == NULL) return NULL;
01946
01947 return st->airport.tile == INVALID_TILE ? NULL : st;
01948 }
01949
01954 void UpdateAirplanesOnNewStation(const Station *st)
01955 {
01956
01957 const AirportFTAClass *ap = st->airport.GetFTA();
01958 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01959
01960 Aircraft *v;
01961 FOR_ALL_AIRCRAFT(v) {
01962 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01963 assert(v->state == FLYING);
01964 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01965 UpdateAircraftCache(v);
01966 }
01967 }