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