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