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