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 (v->acache.cached_max_range_sqr != 0) {
00140
00141 const Station *cur_dest = GetTargetAirportIfValid(v);
00142 if (cur_dest != NULL && DistanceSquare(st->airport.tile, cur_dest->airport.tile) > v->acache.cached_max_range_sqr) continue;
00143 }
00144 if (distance < best || index == INVALID_STATION) {
00145 best = distance;
00146 index = st->index;
00147 }
00148 }
00149 return index;
00150 }
00151
00152 SpriteID Aircraft::GetImage(Direction direction, EngineImageType image_type) const
00153 {
00154 uint8 spritenum = this->spritenum;
00155
00156 if (is_custom_sprite(spritenum)) {
00157 SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type);
00158 if (sprite != 0) return sprite;
00159
00160 spritenum = this->GetEngine()->original_image_index;
00161 }
00162
00163 return direction + _aircraft_sprite[spritenum];
00164 }
00165
00166 SpriteID GetRotorImage(const Aircraft *v, EngineImageType image_type)
00167 {
00168 assert(v->subtype == AIR_HELICOPTER);
00169
00170 const Aircraft *w = v->Next()->Next();
00171 if (is_custom_sprite(v->spritenum)) {
00172 SpriteID sprite = GetCustomRotorSprite(v, false, image_type);
00173 if (sprite != 0) return sprite;
00174 }
00175
00176
00177 return SPR_ROTOR_STOPPED + w->state;
00178 }
00179
00180 static SpriteID GetAircraftIcon(EngineID engine, EngineImageType image_type)
00181 {
00182 const Engine *e = Engine::Get(engine);
00183 uint8 spritenum = e->u.air.image_index;
00184
00185 if (is_custom_sprite(spritenum)) {
00186 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
00187 if (sprite != 0) return sprite;
00188
00189 spritenum = e->original_image_index;
00190 }
00191
00192 return DIR_W + _aircraft_sprite[spritenum];
00193 }
00194
00195 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
00196 {
00197 SpriteID sprite = GetAircraftIcon(engine, image_type);
00198 const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00199 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));
00200 DrawSprite(sprite, pal, preferred_x, y);
00201
00202 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00203 SpriteID rotor_sprite = GetCustomRotorIcon(engine, image_type);
00204 if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00205 DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00206 }
00207 }
00208
00218 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
00219 {
00220 const Sprite *spr = GetSprite(GetAircraftIcon(engine, image_type), ST_NORMAL);
00221
00222 width = UnScaleByZoom(spr->width, ZOOM_LVL_GUI);
00223 height = UnScaleByZoom(spr->height, ZOOM_LVL_GUI);
00224 xoffs = UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI);
00225 yoffs = UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI);
00226 }
00227
00237 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
00238 {
00239 const AircraftVehicleInfo *avi = &e->u.air;
00240 const Station *st = Station::GetByTile(tile);
00241
00242
00243 if (!CanVehicleUseStation(e->index, st)) return CMD_ERROR;
00244
00245
00246 tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile));
00247
00248 if (flags & DC_EXEC) {
00249 Aircraft *v = new Aircraft();
00250 Aircraft *u = new Aircraft();
00251 *ret = v;
00252
00253 v->direction = DIR_SE;
00254
00255 v->owner = u->owner = _current_company;
00256
00257 v->tile = tile;
00258
00259 uint x = TileX(tile) * TILE_SIZE + 5;
00260 uint y = TileY(tile) * TILE_SIZE + 3;
00261
00262 v->x_pos = u->x_pos = x;
00263 v->y_pos = u->y_pos = y;
00264
00265 u->z_pos = GetSlopePixelZ(x, y);
00266 v->z_pos = u->z_pos + 1;
00267
00268 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00269 u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00270
00271 v->spritenum = avi->image_index;
00272
00273 v->cargo_cap = avi->passenger_capacity;
00274 v->refit_cap = 0;
00275 u->cargo_cap = avi->mail_capacity;
00276 u->refit_cap = 0;
00277
00278 v->cargo_type = e->GetDefaultCargoType();
00279 u->cargo_type = CT_MAIL;
00280
00281 v->name = NULL;
00282 v->last_station_visited = INVALID_STATION;
00283 v->last_loading_station = INVALID_STATION;
00284
00285 v->acceleration = avi->acceleration;
00286 v->engine_type = e->index;
00287 u->engine_type = e->index;
00288
00289 v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00290 v->UpdateDeltaXY(INVALID_DIR);
00291
00292 u->subtype = AIR_SHADOW;
00293 u->UpdateDeltaXY(INVALID_DIR);
00294
00295 v->reliability = e->reliability;
00296 v->reliability_spd_dec = e->reliability_spd_dec;
00297 v->max_age = e->GetLifeLengthInDays();
00298
00299 _new_vehicle_id = v->index;
00300
00301 v->pos = GetVehiclePosOnBuild(tile);
00302
00303 v->state = HANGAR;
00304 v->previous_pos = v->pos;
00305 v->targetairport = GetStationIndex(tile);
00306 v->SetNext(u);
00307
00308 v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_aircraft);
00309
00310 v->date_of_last_service = _date;
00311 v->build_year = u->build_year = _cur_year;
00312
00313 v->cur_image = u->cur_image = SPR_IMG_QUERY;
00314
00315 v->random_bits = VehicleRandomBits();
00316 u->random_bits = VehicleRandomBits();
00317
00318 v->vehicle_flags = 0;
00319 if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00320 v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
00321
00322 v->InvalidateNewGRFCacheOfChain();
00323
00324 v->cargo_cap = e->DetermineCapacity(v, &u->cargo_cap);
00325
00326 v->InvalidateNewGRFCacheOfChain();
00327
00328 UpdateAircraftCache(v, true);
00329
00330 VehicleUpdatePosition(v);
00331 VehicleUpdatePosition(u);
00332
00333
00334 if (v->subtype == AIR_HELICOPTER) {
00335 Aircraft *w = new Aircraft();
00336 w->engine_type = e->index;
00337 w->direction = DIR_N;
00338 w->owner = _current_company;
00339 w->x_pos = v->x_pos;
00340 w->y_pos = v->y_pos;
00341 w->z_pos = v->z_pos + ROTOR_Z_OFFSET;
00342 w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
00343 w->spritenum = 0xFF;
00344 w->subtype = AIR_ROTOR;
00345 w->cur_image = SPR_ROTOR_STOPPED;
00346 w->random_bits = VehicleRandomBits();
00347
00348 w->state = HRS_ROTOR_STOPPED;
00349 w->UpdateDeltaXY(INVALID_DIR);
00350
00351 u->SetNext(w);
00352 VehicleUpdatePosition(w);
00353 }
00354 }
00355
00356 return CommandCost();
00357 }
00358
00359
00360 bool Aircraft::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00361 {
00362 const Station *st = GetTargetAirportIfValid(this);
00363
00364 if (st == NULL || !st->airport.HasHangar()) {
00365
00366 StationID station = FindNearestHangar(this);
00367
00368 if (station == INVALID_STATION) return false;
00369
00370 st = Station::Get(station);
00371 }
00372
00373 if (location != NULL) *location = st->xy;
00374 if (destination != NULL) *destination = st->index;
00375
00376 return true;
00377 }
00378
00379 static void CheckIfAircraftNeedsService(Aircraft *v)
00380 {
00381 if (Company::Get(v->owner)->settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
00382 if (v->IsChainInDepot()) {
00383 VehicleServiceInDepot(v);
00384 return;
00385 }
00386
00387
00388
00389 if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return;
00390
00391 const Station *st = Station::Get(v->current_order.GetDestination());
00392
00393 assert(st != NULL);
00394
00395
00396 if (st->airport.HasHangar() && CanVehicleUseStation(v, st)) {
00397 v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00398 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00399 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00400 v->current_order.MakeDummy();
00401 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00402 }
00403 }
00404
00405 Money Aircraft::GetRunningCost() const
00406 {
00407 const Engine *e = this->GetEngine();
00408 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00409 return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
00410 }
00411
00412 void Aircraft::OnNewDay()
00413 {
00414 if (!this->IsNormalAircraft()) return;
00415
00416 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00417
00418 CheckOrders(this);
00419
00420 CheckVehicleBreakdown(this);
00421 AgeVehicle(this);
00422 CheckIfAircraftNeedsService(this);
00423
00424 if (this->running_ticks == 0) return;
00425
00426 CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00427
00428 this->profit_this_year -= cost.GetCost();
00429 this->running_ticks = 0;
00430
00431 SubtractMoneyFromCompanyFract(this->owner, cost);
00432
00433 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00434 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00435 }
00436
00437 static void HelicopterTickHandler(Aircraft *v)
00438 {
00439 Aircraft *u = v->Next()->Next();
00440
00441 if (u->vehstatus & VS_HIDDEN) return;
00442
00443
00444
00445 if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00446 if (u->cur_speed != 0) {
00447 u->cur_speed++;
00448 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00449 u->cur_speed = 0;
00450 }
00451 }
00452 } else {
00453 if (u->cur_speed == 0) {
00454 u->cur_speed = 0x70;
00455 }
00456 if (u->cur_speed >= 0x50) {
00457 u->cur_speed--;
00458 }
00459 }
00460
00461 int tick = ++u->tick_counter;
00462 int spd = u->cur_speed >> 4;
00463
00464 SpriteID img;
00465 if (spd == 0) {
00466 u->state = HRS_ROTOR_STOPPED;
00467 img = GetRotorImage(v, EIT_ON_MAP);
00468 if (u->cur_image == img) return;
00469 } else if (tick >= spd) {
00470 u->tick_counter = 0;
00471 u->state++;
00472 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00473 img = GetRotorImage(v, EIT_ON_MAP);
00474 } else {
00475 return;
00476 }
00477
00478 u->cur_image = img;
00479
00480 VehicleUpdatePositionAndViewport(u);
00481 }
00482
00490 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00491 {
00492 v->x_pos = x;
00493 v->y_pos = y;
00494 v->z_pos = z;
00495
00496 VehicleUpdatePosition(v);
00497 v->UpdateViewport(true, false);
00498 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v, EIT_ON_MAP);
00499
00500 Aircraft *u = v->Next();
00501
00502 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00503 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00504 u->x_pos = x;
00505 u->y_pos = y - ((v->z_pos - GetSlopePixelZ(safe_x, safe_y)) >> 3);
00506
00507 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00508 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
00509 u->cur_image = v->cur_image;
00510
00511 VehicleUpdatePositionAndViewport(u);
00512
00513 u = u->Next();
00514 if (u != NULL) {
00515 u->x_pos = x;
00516 u->y_pos = y;
00517 u->z_pos = z + ROTOR_Z_OFFSET;
00518
00519 VehicleUpdatePositionAndViewport(u);
00520 }
00521 }
00522
00527 void HandleAircraftEnterHangar(Aircraft *v)
00528 {
00529 v->subspeed = 0;
00530 v->progress = 0;
00531
00532 Aircraft *u = v->Next();
00533 u->vehstatus |= VS_HIDDEN;
00534 u = u->Next();
00535 if (u != NULL) {
00536 u->vehstatus |= VS_HIDDEN;
00537 u->cur_speed = 0;
00538 }
00539
00540 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00541 }
00542
00543 static void PlayAircraftSound(const Vehicle *v)
00544 {
00545 if (!PlayVehicleSound(v, VSE_START)) {
00546 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00547 }
00548 }
00549
00550
00557 void UpdateAircraftCache(Aircraft *v, bool update_range)
00558 {
00559 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00560 if (max_speed != 0) {
00561
00562 max_speed = (max_speed * 128) / 10;
00563
00564 v->vcache.cached_max_speed = max_speed;
00565 } else {
00566
00567 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00568 }
00569
00570
00571 v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
00572 Aircraft *u = v->Next();
00573 u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
00574
00575
00576 if (update_range) {
00577 v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
00578
00579 v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
00580 }
00581 }
00582
00583
00587 enum AircraftSpeedLimits {
00588 SPEED_LIMIT_TAXI = 50,
00589 SPEED_LIMIT_APPROACH = 230,
00590 SPEED_LIMIT_BROKEN = 320,
00591 SPEED_LIMIT_HOLD = 425,
00592 SPEED_LIMIT_NONE = 0xFFFF,
00593 };
00594
00602 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00603 {
00611 uint spd = v->acceleration * 77;
00612 byte t;
00613
00614
00615
00616 speed_limit *= _settings_game.vehicle.plane_speed;
00617
00618 if (v->vcache.cached_max_speed < speed_limit) {
00619 if (v->cur_speed < speed_limit) hard_limit = false;
00620 speed_limit = v->vcache.cached_max_speed;
00621 }
00622
00623 v->subspeed = (t = v->subspeed) + (byte)spd;
00624
00625
00626
00627
00628
00629
00630
00631 if (!hard_limit && v->cur_speed > speed_limit) {
00632 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00633 }
00634
00635 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00636
00637
00638 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00639
00640
00641 if (spd != v->cur_speed) {
00642 v->cur_speed = spd;
00643 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
00644 }
00645
00646
00647 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00648
00649
00650 spd = v->GetOldAdvanceSpeed(spd);
00651
00652 spd += v->progress;
00653 v->progress = (byte)spd;
00654 return spd >> 8;
00655 }
00656
00664 int GetAircraftFlyingAltitude(const Aircraft *v)
00665 {
00666 if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00667
00668
00669
00670
00671 int base_altitude = PLANE_HOLDING_ALTITUDE;
00672
00673
00674
00675
00676 switch (v->direction) {
00677 case DIR_N:
00678 case DIR_NE:
00679 case DIR_E:
00680 case DIR_SE:
00681 base_altitude += 10;
00682 break;
00683
00684 default: break;
00685 }
00686
00687
00688 base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00689
00690 return base_altitude;
00691 }
00692
00707 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00708 {
00709 assert(v != NULL);
00710 assert(apc != NULL);
00711
00712
00713
00714
00715 TileIndex tile = 0;
00716
00717 const Station *st = Station::GetIfValid(v->targetairport);
00718 if (st != NULL) {
00719
00720 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00721 }
00722
00723 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00724 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00725
00726 DiagDirection dir;
00727 if (abs(delta_y) < abs(delta_x)) {
00728
00729 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00730 } else {
00731
00732 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00733 }
00734 dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00735 return apc->entry_points[dir];
00736 }
00737
00738
00739 static void MaybeCrashAirplane(Aircraft *v);
00740
00748 static bool AircraftController(Aircraft *v)
00749 {
00750 int count;
00751
00752
00753 const Station *st = Station::GetIfValid(v->targetairport);
00754
00755 TileIndex tile = INVALID_TILE;
00756 Direction rotation = DIR_N;
00757 uint size_x = 1, size_y = 1;
00758 if (st != NULL) {
00759 if (st->airport.tile != INVALID_TILE) {
00760 tile = st->airport.tile;
00761 rotation = st->airport.rotation;
00762 size_x = st->airport.w;
00763 size_y = st->airport.h;
00764 } else {
00765 tile = st->xy;
00766 }
00767 }
00768
00769 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00770
00771
00772 if (st == NULL || st->airport.tile == INVALID_TILE) {
00773
00774 if (v->pos >= afc->nofelements) {
00775 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00776 } else if (v->targetairport != v->current_order.GetDestination()) {
00777
00778 v->state = FLYING;
00779 UpdateAircraftCache(v);
00780 AircraftNextAirportPos_and_Order(v);
00781
00782 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00783 return false;
00784 }
00785 }
00786
00787
00788 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00789
00790 int x = TileX(tile) * TILE_SIZE;
00791 int y = TileY(tile) * TILE_SIZE;
00792
00793
00794 if (amd.flag & AMED_HELI_RAISE) {
00795 Aircraft *u = v->Next()->Next();
00796
00797
00798 if (u->cur_speed > 32) {
00799 v->cur_speed = 0;
00800 if (--u->cur_speed == 32) {
00801 if (!PlayVehicleSound(v, VSE_START)) {
00802 SndPlayVehicleFx(SND_18_HELICOPTER, v);
00803 }
00804 }
00805 } else {
00806 u->cur_speed = 32;
00807 count = UpdateAircraftSpeed(v);
00808 if (count > 0) {
00809 v->tile = 0;
00810 int z_dest = GetAircraftFlyingAltitude(v);
00811
00812
00813 if (v->z_pos >= z_dest) {
00814 v->cur_speed = 0;
00815 return true;
00816 }
00817 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00818 }
00819 }
00820 return false;
00821 }
00822
00823
00824 if (amd.flag & AMED_HELI_LOWER) {
00825 if (st == NULL) {
00826
00827
00828
00829 v->state = FLYING;
00830 UpdateAircraftCache(v);
00831 AircraftNextAirportPos_and_Order(v);
00832 return false;
00833 }
00834
00835
00836 v->tile = tile;
00837
00838
00839 int z = GetSlopePixelZ(x, y) + 1 + afc->delta_z;
00840
00841 if (z == v->z_pos) {
00842 Vehicle *u = v->Next()->Next();
00843
00844
00845 if (u->cur_speed >= 80) return true;
00846 u->cur_speed += 4;
00847 } else {
00848 count = UpdateAircraftSpeed(v);
00849 if (count > 0) {
00850 if (v->z_pos > z) {
00851 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00852 } else {
00853 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00854 }
00855 }
00856 }
00857 return false;
00858 }
00859
00860
00861 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
00862
00863
00864 if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00865
00866
00867 if (dist == 0) {
00868
00869 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00870
00871
00872 if (dirdiff == DIRDIFF_SAME) {
00873 v->cur_speed = 0;
00874 return true;
00875 }
00876
00877 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00878
00879 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00880 v->cur_speed >>= 1;
00881
00882 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00883 return false;
00884 }
00885
00886 if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00887 MaybeCrashAirplane(v);
00888 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00889 }
00890
00891 uint speed_limit = SPEED_LIMIT_TAXI;
00892 bool hard_limit = true;
00893
00894 if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00895 if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00896 if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00897 if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00898
00899 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00900 if (count == 0) return false;
00901
00902 if (v->turn_counter != 0) v->turn_counter--;
00903
00904 do {
00905
00906 GetNewVehiclePosResult gp;
00907
00908 if (dist < 4 || (amd.flag & AMED_LAND)) {
00909
00910 gp.x = (v->x_pos != (x + amd.x)) ?
00911 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00912 v->x_pos;
00913 gp.y = (v->y_pos != (y + amd.y)) ?
00914 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00915 v->y_pos;
00916
00917
00918 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00919
00920 } else {
00921
00922
00923 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00924 if (newdir != v->direction) {
00925 if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00926 if (v->turn_counter == 0 || newdir == v->last_direction) {
00927 if (newdir == v->last_direction) {
00928 v->number_consecutive_turns = 0;
00929 } else {
00930 v->number_consecutive_turns++;
00931 }
00932 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00933 v->last_direction = v->direction;
00934 v->direction = newdir;
00935 }
00936
00937
00938 gp = GetNewVehiclePos(v);
00939 } else {
00940 v->cur_speed >>= 1;
00941 v->direction = newdir;
00942
00943
00944
00945
00946
00947
00948 gp.x = v->x_pos;
00949 gp.y = v->y_pos;
00950 gp.new_tile = gp.old_tile = v->tile;
00951 }
00952 } else {
00953 v->number_consecutive_turns = 0;
00954
00955 gp = GetNewVehiclePos(v);
00956 }
00957 }
00958
00959 v->tile = gp.new_tile;
00960
00961 if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00962
00963
00964 int z = v->z_pos;
00965
00966 if (amd.flag & AMED_TAKEOFF) {
00967 z = min(z + 2, GetAircraftFlyingAltitude(v));
00968 }
00969
00970
00971 if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00972
00973 if (amd.flag & AMED_LAND) {
00974 if (st->airport.tile == INVALID_TILE) {
00975
00976 v->state = FLYING;
00977 UpdateAircraftCache(v);
00978 AircraftNextAirportPos_and_Order(v);
00979
00980 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00981 continue;
00982 }
00983
00984 int curz = GetSlopePixelZ(x + amd.x, y + amd.y) + 1;
00985
00986
00987 assert(curz <= z);
00988 int t = max(1U, dist - 4);
00989 int delta = z - curz;
00990
00991
00992 if (delta >= t) {
00993 z -= CeilDiv(z - curz, t);
00994 }
00995 if (z < curz) z = curz;
00996 }
00997
00998
00999 if (amd.flag & AMED_BRAKE) {
01000 int curz = GetSlopePixelZ(x, y) + 1;
01001
01002 if (z > curz) {
01003 z--;
01004 } else if (z < curz) {
01005 z++;
01006 }
01007
01008 }
01009
01010 SetAircraftPosition(v, gp.x, gp.y, z);
01011 } while (--count != 0);
01012 return false;
01013 }
01014
01019 static bool HandleCrashedAircraft(Aircraft *v)
01020 {
01021 v->crashed_counter += 3;
01022
01023 Station *st = GetTargetAirportIfValid(v);
01024
01025
01026 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01027 int z = GetSlopePixelZ(v->x_pos, v->y_pos);
01028 v->z_pos -= 1;
01029 if (v->z_pos == z) {
01030 v->crashed_counter = 500;
01031 v->z_pos++;
01032 }
01033 }
01034
01035 if (v->crashed_counter < 650) {
01036 uint32 r;
01037 if (Chance16R(1, 32, r)) {
01038 static const DirDiff delta[] = {
01039 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01040 };
01041
01042 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01043 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01044 r = Random();
01045 CreateEffectVehicleRel(v,
01046 GB(r, 0, 4) - 4,
01047 GB(r, 4, 4) - 4,
01048 GB(r, 8, 4),
01049 EV_EXPLOSION_SMALL);
01050 }
01051 } else if (v->crashed_counter >= 10000) {
01052
01053
01054
01055
01056
01057 if (st != NULL) {
01058 CLRBITS(st->airport.flags, RUNWAY_IN_block);
01059 CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block);
01060 CLRBITS(st->airport.flags, RUNWAY_IN2_block);
01061 }
01062
01063 delete v;
01064
01065 return false;
01066 }
01067
01068 return true;
01069 }
01070
01071
01077 static void HandleAircraftSmoke(Aircraft *v, bool mode)
01078 {
01079 static const struct {
01080 int8 x;
01081 int8 y;
01082 } smoke_pos[] = {
01083 { 5, 5 },
01084 { 6, 0 },
01085 { 5, -5 },
01086 { 0, -6 },
01087 { -5, -5 },
01088 { -6, 0 },
01089 { -5, 5 },
01090 { 0, 6 }
01091 };
01092
01093 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01094
01095
01096 if (v->cur_speed < 10) {
01097 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01098 v->breakdown_ctr = 0;
01099 return;
01100 }
01101
01102
01103 if (!mode && (v->tick_counter & 0x0F) == 0) {
01104 CreateEffectVehicleRel(v,
01105 smoke_pos[v->direction].x,
01106 smoke_pos[v->direction].y,
01107 2,
01108 EV_BREAKDOWN_SMOKE_AIRCRAFT
01109 );
01110 }
01111 }
01112
01113 void HandleMissingAircraftOrders(Aircraft *v)
01114 {
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130 const Station *st = GetTargetAirportIfValid(v);
01131 if (st == NULL) {
01132 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01133 CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01134 cur_company.Restore();
01135
01136 if (ret.Failed()) CrashAirplane(v);
01137 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01138 v->current_order.Free();
01139 }
01140 }
01141
01142
01143 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01144 {
01145
01146 if (this->state == FLYING) {
01147 AircraftNextAirportPos_and_Order(this);
01148 }
01149
01150
01151 return 0;
01152 }
01153
01154 void Aircraft::MarkDirty()
01155 {
01156 this->UpdateViewport(false, false);
01157 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this, EIT_ON_MAP);
01158 }
01159
01160
01161 uint Aircraft::Crash(bool flooded)
01162 {
01163 uint pass = Vehicle::Crash(flooded) + 2;
01164 this->crashed_counter = flooded ? 9000 : 0;
01165
01166 return pass;
01167 }
01168
01173 static void CrashAirplane(Aircraft *v)
01174 {
01175 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01176
01177 uint pass = v->Crash();
01178 SetDParam(0, pass);
01179
01180 v->cargo.Truncate();
01181 v->Next()->cargo.Truncate();
01182 const Station *st = GetTargetAirportIfValid(v);
01183 StringID newsitem;
01184 if (st == NULL) {
01185 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01186 } else {
01187 SetDParam(1, st->index);
01188 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01189 }
01190
01191 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01192 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, st == NULL ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING));
01193
01194 AddVehicleNewsItem(newsitem, NT_ACCIDENT, v->index, st != NULL ? st->index : INVALID_STATION);
01195
01196 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01197 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
01198 }
01199
01204 static void MaybeCrashAirplane(Aircraft *v)
01205 {
01206 if (_settings_game.vehicle.plane_crashes == 0) return;
01207
01208 Station *st = Station::Get(v->targetairport);
01209
01210
01211 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01212 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01213 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01214 !_cheats.no_jetcrash.value) {
01215 prob /= 20;
01216 } else {
01217 prob /= 1500;
01218 }
01219
01220 if (GB(Random(), 0, 22) > prob) return;
01221
01222
01223 for (CargoID i = 0; i < NUM_CARGO; i++) {
01224 st->goods[i].rating = 1;
01225 st->goods[i].cargo.Truncate();
01226 }
01227
01228 CrashAirplane(v);
01229 }
01230
01236 static void AircraftEntersTerminal(Aircraft *v)
01237 {
01238 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01239
01240 Station *st = Station::Get(v->targetairport);
01241 v->last_station_visited = v->targetairport;
01242
01243
01244 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01245 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01246 SetDParam(0, st->index);
01247
01248 AddVehicleNewsItem(
01249 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01250 (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
01251 v->index,
01252 st->index
01253 );
01254 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01255 Game::NewEvent(new ScriptEventStationFirstVehicle(st->index, v->index));
01256 }
01257
01258 v->BeginLoading();
01259 }
01260
01265 static void AircraftLandAirplane(Aircraft *v)
01266 {
01267 v->UpdateDeltaXY(INVALID_DIR);
01268
01269 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01270 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01271 }
01272 }
01273
01274
01276 void AircraftNextAirportPos_and_Order(Aircraft *v)
01277 {
01278 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01279 v->targetairport = v->current_order.GetDestination();
01280 }
01281
01282 const Station *st = GetTargetAirportIfValid(v);
01283 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01284 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01285 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01286 }
01287
01296 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01297 {
01298 v->cur_speed = 0;
01299 v->subspeed = 0;
01300 v->progress = 0;
01301 v->direction = exit_dir;
01302 v->vehstatus &= ~VS_HIDDEN;
01303 {
01304 Vehicle *u = v->Next();
01305 u->vehstatus &= ~VS_HIDDEN;
01306
01307
01308 u = u->Next();
01309 if (u != NULL) {
01310 u->vehstatus &= ~VS_HIDDEN;
01311 u->cur_speed = 80;
01312 }
01313 }
01314
01315 VehicleServiceInDepot(v);
01316 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01317 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01318 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01319 }
01320
01324 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01325 {
01326 AircraftEntersTerminal(v);
01327 v->state = apc->layout[v->pos].heading;
01328 }
01329
01335 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01336 {
01337 VehicleEnterDepot(v);
01338 v->state = apc->layout[v->pos].heading;
01339 }
01340
01346 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01347 {
01348
01349 if (v->previous_pos != v->pos) {
01350 AircraftEventHandler_EnterHangar(v, apc);
01351 return;
01352 }
01353
01354
01355 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01356 v->current_order.Free();
01357 return;
01358 }
01359
01360 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01361 !v->current_order.IsType(OT_GOTO_DEPOT))
01362 return;
01363
01364
01365 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01366 VehicleEnterDepot(v);
01367 return;
01368 }
01369
01370
01371 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01372
01373
01374 if (v->current_order.GetDestination() == v->targetairport) {
01375
01376
01377 if (v->subtype == AIR_HELICOPTER) {
01378 if (!AirportFindFreeHelipad(v, apc)) return;
01379 } else {
01380 if (!AirportFindFreeTerminal(v, apc)) return;
01381 }
01382 } else {
01383
01384 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01385 }
01386 const Station *st = Station::GetByTile(v->tile);
01387 AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01388 AirportMove(v, apc);
01389 }
01390
01392 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01393 {
01394
01395 if (v->previous_pos != v->pos) {
01396 AircraftEventHandler_EnterTerminal(v, apc);
01397
01398
01399 if (_settings_game.order.serviceathelipad) {
01400 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01401
01402 v->date_of_last_service = _date;
01403 v->breakdowns_since_last_service = 0;
01404 v->reliability = v->GetEngine()->reliability;
01405 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01406 }
01407 }
01408 return;
01409 }
01410
01411 if (v->current_order.IsType(OT_NOTHING)) return;
01412
01413
01414 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01415
01416
01417
01418
01419 bool go_to_hangar = false;
01420 switch (v->current_order.GetType()) {
01421 case OT_GOTO_STATION:
01422 break;
01423 case OT_GOTO_DEPOT:
01424 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01425 break;
01426 case OT_CONDITIONAL:
01427
01428
01429
01430 return;
01431 default:
01432 v->current_order.Free();
01433 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01434 }
01435
01436 if (go_to_hangar) {
01437 v->state = HANGAR;
01438 } else {
01439
01440 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01441 }
01442 AirportMove(v, apc);
01443 }
01444
01445 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01446 {
01447 error("OK, you shouldn't be here, check your Airport Scheme!");
01448 }
01449
01450 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01451 {
01452 PlayAircraftSound(v);
01453 v->state = STARTTAKEOFF;
01454 }
01455
01456 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01457 {
01458 v->state = ENDTAKEOFF;
01459 v->UpdateDeltaXY(INVALID_DIR);
01460 }
01461
01462 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01463 {
01464 v->state = FLYING;
01465
01466 AircraftNextAirportPos_and_Order(v);
01467 }
01468
01469 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01470 {
01471 v->state = FLYING;
01472 v->UpdateDeltaXY(INVALID_DIR);
01473
01474
01475 AircraftNextAirportPos_and_Order(v);
01476
01477
01478 if (v->NeedsAutomaticServicing()) {
01479 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01480 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01481 cur_company.Restore();
01482 }
01483 }
01484
01485 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01486 {
01487 Station *st = Station::Get(v->targetairport);
01488
01489
01490 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) {
01491
01492
01493
01494 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01495 const AirportFTA *current = apc->layout[v->pos].next;
01496 while (current != NULL) {
01497 if (current->heading == landingtype) {
01498
01499
01500
01501 uint16 tcur_speed = v->cur_speed;
01502 uint16 tsubspeed = v->subspeed;
01503 if (!AirportHasBlock(v, current, apc)) {
01504 v->state = landingtype;
01505
01506
01507
01508 v->pos = current->next_position;
01509 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01510 return;
01511 }
01512 v->cur_speed = tcur_speed;
01513 v->subspeed = tsubspeed;
01514 }
01515 current = current->next;
01516 }
01517 }
01518 v->state = FLYING;
01519 v->pos = apc->layout[v->pos].next_position;
01520 }
01521
01522 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01523 {
01524 v->state = ENDLANDING;
01525 AircraftLandAirplane(v);
01526
01527
01528 if (v->NeedsAutomaticServicing()) {
01529 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01530 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01531 cur_company.Restore();
01532 }
01533 }
01534
01535 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01536 {
01537 v->state = HELIENDLANDING;
01538 v->UpdateDeltaXY(INVALID_DIR);
01539 }
01540
01541 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01542 {
01543
01544 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01545
01546
01547
01548
01549
01550 if (v->current_order.IsType(OT_GOTO_STATION)) {
01551 if (AirportFindFreeTerminal(v, apc)) return;
01552 }
01553 v->state = HANGAR;
01554
01555 }
01556
01557 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01558 {
01559
01560 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01561
01562
01563
01564
01565
01566
01567
01568
01569 if (v->current_order.IsType(OT_GOTO_STATION)) {
01570 if (AirportFindFreeHelipad(v, apc)) return;
01571 }
01572 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01573 }
01574
01580 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01582 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01583 AircraftEventHandler_General,
01584 AircraftEventHandler_InHangar,
01585 AircraftEventHandler_AtTerminal,
01586 AircraftEventHandler_AtTerminal,
01587 AircraftEventHandler_AtTerminal,
01588 AircraftEventHandler_AtTerminal,
01589 AircraftEventHandler_AtTerminal,
01590 AircraftEventHandler_AtTerminal,
01591 AircraftEventHandler_AtTerminal,
01592 AircraftEventHandler_AtTerminal,
01593 AircraftEventHandler_TakeOff,
01594 AircraftEventHandler_StartTakeOff,
01595 AircraftEventHandler_EndTakeOff,
01596 AircraftEventHandler_HeliTakeOff,
01597 AircraftEventHandler_Flying,
01598 AircraftEventHandler_Landing,
01599 AircraftEventHandler_EndLanding,
01600 AircraftEventHandler_HeliLanding,
01601 AircraftEventHandler_HeliEndLanding,
01602 AircraftEventHandler_AtTerminal,
01603 AircraftEventHandler_AtTerminal,
01604 AircraftEventHandler_AtTerminal,
01605 };
01606
01607 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01608 {
01609
01610 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01611 Station *st = Station::Get(v->targetairport);
01612
01613 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01614 }
01615 }
01616
01617 static void AirportGoToNextPosition(Aircraft *v)
01618 {
01619
01620 if (!AircraftController(v)) return;
01621
01622 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01623
01624 AirportClearBlock(v, apc);
01625 AirportMove(v, apc);
01626 }
01627
01628
01629 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01630 {
01631
01632 if (v->pos >= apc->nofelements) {
01633 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01634 assert(v->pos < apc->nofelements);
01635 }
01636
01637 const AirportFTA *current = &apc->layout[v->pos];
01638
01639 if (current->heading == v->state) {
01640 byte prev_pos = v->pos;
01641 byte prev_state = v->state;
01642 _aircraft_state_handlers[v->state](v, apc);
01643 if (v->state != FLYING) v->previous_pos = prev_pos;
01644 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01645 return true;
01646 }
01647
01648 v->previous_pos = v->pos;
01649
01650
01651 if (current->next == NULL) {
01652 if (AirportSetBlocks(v, current, apc)) {
01653 v->pos = current->next_position;
01654 UpdateAircraftCache(v);
01655 }
01656 return false;
01657 }
01658
01659
01660
01661 do {
01662 if (v->state == current->heading || current->heading == TO_ALL) {
01663 if (AirportSetBlocks(v, current, apc)) {
01664 v->pos = current->next_position;
01665 UpdateAircraftCache(v);
01666 }
01667 return false;
01668 }
01669 current = current->next;
01670 } while (current != NULL);
01671
01672 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01673 NOT_REACHED();
01674 }
01675
01677 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01678 {
01679 const AirportFTA *reference = &apc->layout[v->pos];
01680 const AirportFTA *next = &apc->layout[current_pos->next_position];
01681
01682
01683 if (apc->layout[current_pos->position].block != next->block) {
01684 const Station *st = Station::Get(v->targetairport);
01685 uint64 airport_flags = next->block;
01686
01687
01688 if (current_pos != reference && current_pos->block != NOTHING_block) {
01689 airport_flags |= current_pos->block;
01690 }
01691
01692 if (st->airport.flags & airport_flags) {
01693 v->cur_speed = 0;
01694 v->subspeed = 0;
01695 return true;
01696 }
01697 }
01698 return false;
01699 }
01700
01708 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01709 {
01710 const AirportFTA *next = &apc->layout[current_pos->next_position];
01711 const AirportFTA *reference = &apc->layout[v->pos];
01712
01713
01714 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01715 uint64 airport_flags = next->block;
01716
01717
01718 const AirportFTA *current = current_pos;
01719 if (current == reference) current = current->next;
01720 while (current != NULL) {
01721 if (current->heading == current_pos->heading && current->block != 0) {
01722 airport_flags |= current->block;
01723 break;
01724 }
01725 current = current->next;
01726 }
01727
01728
01729
01730 if (current_pos->block == next->block) airport_flags ^= next->block;
01731
01732 Station *st = Station::Get(v->targetairport);
01733 if (st->airport.flags & airport_flags) {
01734 v->cur_speed = 0;
01735 v->subspeed = 0;
01736 return false;
01737 }
01738
01739 if (next->block != NOTHING_block) {
01740 SETBITS(st->airport.flags, airport_flags);
01741 }
01742 }
01743 return true;
01744 }
01745
01750 struct MovementTerminalMapping {
01751 AirportMovementStates state;
01752 uint64 airport_flag;
01753 };
01754
01756 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01757 {TERM1, TERM1_block},
01758 {TERM2, TERM2_block},
01759 {TERM3, TERM3_block},
01760 {TERM4, TERM4_block},
01761 {TERM5, TERM5_block},
01762 {TERM6, TERM6_block},
01763 {TERM7, TERM7_block},
01764 {TERM8, TERM8_block},
01765 {HELIPAD1, HELIPAD1_block},
01766 {HELIPAD2, HELIPAD2_block},
01767 {HELIPAD3, HELIPAD3_block},
01768 };
01769
01777 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01778 {
01779 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01780 Station *st = Station::Get(v->targetairport);
01781 for (; i < last_terminal; i++) {
01782 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01783
01784 v->state = _airport_terminal_mapping[i].state;
01785 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01786 return true;
01787 }
01788 }
01789 return false;
01790 }
01791
01797 static uint GetNumTerminals(const AirportFTAClass *apc)
01798 {
01799 uint num = 0;
01800
01801 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01802
01803 return num;
01804 }
01805
01812 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01813 {
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824 if (apc->terminals[0] > 1) {
01825 const Station *st = Station::Get(v->targetairport);
01826 const AirportFTA *temp = apc->layout[v->pos].next;
01827
01828 while (temp != NULL) {
01829 if (temp->heading == 255) {
01830 if (!(st->airport.flags & temp->block)) {
01831
01832
01833 uint target_group = temp->next_position + 1;
01834
01835
01836
01837
01838 uint group_start = 0;
01839 for (uint i = 1; i < target_group; i++) {
01840 group_start += apc->terminals[i];
01841 }
01842
01843 uint group_end = group_start + apc->terminals[target_group];
01844 if (FreeTerminal(v, group_start, group_end)) return true;
01845 }
01846 } else {
01847
01848
01849 return false;
01850 }
01851 temp = temp->next;
01852 }
01853 }
01854
01855
01856 return FreeTerminal(v, 0, GetNumTerminals(apc));
01857 }
01858
01865 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01866 {
01867
01868 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01869
01870
01871
01872 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01873 }
01874
01880 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01881 {
01882 if (too_far) {
01883 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01884 SetBit(v->flags, VAF_DEST_TOO_FAR);
01885 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01886 AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01887 if (v->owner == _local_company) {
01888
01889 SetDParam(0, v->index);
01890 AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index);
01891 }
01892 }
01893 return;
01894 }
01895
01896 if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01897
01898 ClrBit(v->flags, VAF_DEST_TOO_FAR);
01899 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
01900 DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01901 }
01902 }
01903
01904 static bool AircraftEventHandler(Aircraft *v, int loop)
01905 {
01906 if (v->vehstatus & VS_CRASHED) {
01907 return HandleCrashedAircraft(v);
01908 }
01909
01910 if (v->vehstatus & VS_STOPPED) return true;
01911
01912 v->HandleBreakdown();
01913
01914 HandleAircraftSmoke(v, loop != 0);
01915 ProcessOrders(v);
01916 v->HandleLoading(loop != 0);
01917
01918 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01919
01920 if (v->state >= ENDTAKEOFF && v->state <= HELIENDLANDING) {
01921
01922 AircraftHandleDestTooFar(v, false);
01923 } else if (v->acache.cached_max_range_sqr != 0) {
01924
01925
01926 Station *cur_st = Station::GetIfValid(v->targetairport);
01927 Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01928
01929 if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01930 uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01931 AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01932 }
01933 }
01934
01935 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01936
01937 return true;
01938 }
01939
01940 bool Aircraft::Tick()
01941 {
01942 if (!this->IsNormalAircraft()) return true;
01943
01944 this->tick_counter++;
01945
01946 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01947
01948 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01949
01950 this->current_order_time++;
01951
01952 for (uint i = 0; i != 2; i++) {
01953
01954 if (!AircraftEventHandler(this, i)) return false;
01955 }
01956
01957 return true;
01958 }
01959
01960
01967 Station *GetTargetAirportIfValid(const Aircraft *v)
01968 {
01969 assert(v->type == VEH_AIRCRAFT);
01970
01971 Station *st = Station::GetIfValid(v->targetairport);
01972 if (st == NULL) return NULL;
01973
01974 return st->airport.tile == INVALID_TILE ? NULL : st;
01975 }
01976
01981 void UpdateAirplanesOnNewStation(const Station *st)
01982 {
01983
01984 const AirportFTAClass *ap = st->airport.GetFTA();
01985 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01986
01987 Aircraft *v;
01988 FOR_ALL_AIRCRAFT(v) {
01989 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01990 assert(v->state == FLYING);
01991 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01992 UpdateAircraftCache(v);
01993 }
01994 }