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