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