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