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 "vehicle_gui.h"
00020 #include "newgrf_engine.h"
00021 #include "newgrf_sound.h"
00022 #include "spritecache.h"
00023 #include "strings_func.h"
00024 #include "command_func.h"
00025 #include "window_func.h"
00026 #include "date_func.h"
00027 #include "vehicle_func.h"
00028 #include "sound_func.h"
00029 #include "cheat_type.h"
00030 #include "company_base.h"
00031 #include "ai/ai.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 VehicleMove(v, false);
00318 VehicleMove(u, false);
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 VehicleMove(w, false);
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, VVW_WIDGET_START_STOP_VEH);
00386 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00387 v->current_order.MakeDummy();
00388 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
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 VehicleMove(u, true);
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 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 VehicleMove(u, true);
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 VehicleMove(u, true);
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, VVW_WIDGET_START_STOP_VEH);
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
01165 AddVehicleNewsItem(newsitem,
01166 NS_ACCIDENT,
01167 v->index,
01168 st != NULL ? st->index : INVALID_STATION);
01169
01170 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01171 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01172 }
01173
01178 static void MaybeCrashAirplane(Aircraft *v)
01179 {
01180 if (_settings_game.vehicle.plane_crashes == 0) return;
01181
01182 Station *st = Station::Get(v->targetairport);
01183
01184
01185 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01186 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01187 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01188 !_cheats.no_jetcrash.value) {
01189 prob /= 20;
01190 } else {
01191 prob /= 1500;
01192 }
01193
01194 if (GB(Random(), 0, 22) > prob) return;
01195
01196
01197 for (CargoID i = 0; i < NUM_CARGO; i++) {
01198 st->goods[i].rating = 1;
01199 st->goods[i].cargo.Truncate(0);
01200 }
01201
01202 CrashAirplane(v);
01203 }
01204
01210 static void AircraftEntersTerminal(Aircraft *v)
01211 {
01212 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01213
01214 Station *st = Station::Get(v->targetairport);
01215 v->last_station_visited = v->targetairport;
01216
01217
01218 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01219 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01220 SetDParam(0, st->index);
01221
01222 AddVehicleNewsItem(
01223 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01224 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01225 v->index,
01226 st->index
01227 );
01228 AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
01229 }
01230
01231 v->BeginLoading();
01232 }
01233
01238 static void AircraftLandAirplane(Aircraft *v)
01239 {
01240 v->UpdateDeltaXY(INVALID_DIR);
01241
01242 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01243 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01244 }
01245 }
01246
01247
01249 void AircraftNextAirportPos_and_Order(Aircraft *v)
01250 {
01251 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01252 v->targetairport = v->current_order.GetDestination();
01253 }
01254
01255 const Station *st = GetTargetAirportIfValid(v);
01256 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01257 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01258 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01259 }
01260
01269 void AircraftLeaveHangar(Aircraft *v, Direction exit_dir)
01270 {
01271 v->cur_speed = 0;
01272 v->subspeed = 0;
01273 v->progress = 0;
01274 v->direction = exit_dir;
01275 v->vehstatus &= ~VS_HIDDEN;
01276 {
01277 Vehicle *u = v->Next();
01278 u->vehstatus &= ~VS_HIDDEN;
01279
01280
01281 u = u->Next();
01282 if (u != NULL) {
01283 u->vehstatus &= ~VS_HIDDEN;
01284 u->cur_speed = 80;
01285 }
01286 }
01287
01288 VehicleServiceInDepot(v);
01289 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01290 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01291 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01292 }
01293
01297 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01298 {
01299 AircraftEntersTerminal(v);
01300 v->state = apc->layout[v->pos].heading;
01301 }
01302
01308 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01309 {
01310 VehicleEnterDepot(v);
01311 v->state = apc->layout[v->pos].heading;
01312 }
01313
01319 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01320 {
01321
01322 if (v->previous_pos != v->pos) {
01323 AircraftEventHandler_EnterHangar(v, apc);
01324 return;
01325 }
01326
01327
01328 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01329 v->current_order.Free();
01330 return;
01331 }
01332
01333 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01334 !v->current_order.IsType(OT_GOTO_DEPOT))
01335 return;
01336
01337
01338 if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
01339 VehicleEnterDepot(v);
01340 return;
01341 }
01342
01343
01344 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01345
01346
01347 if (v->current_order.GetDestination() == v->targetairport) {
01348
01349
01350 if (v->subtype == AIR_HELICOPTER) {
01351 if (!AirportFindFreeHelipad(v, apc)) return;
01352 } else {
01353 if (!AirportFindFreeTerminal(v, apc)) return;
01354 }
01355 } else {
01356
01357 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01358 }
01359 const Station *st = Station::GetByTile(v->tile);
01360 AircraftLeaveHangar(v, st->airport.GetHangarExitDirection(v->tile));
01361 AirportMove(v, apc);
01362 }
01363
01365 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01366 {
01367
01368 if (v->previous_pos != v->pos) {
01369 AircraftEventHandler_EnterTerminal(v, apc);
01370
01371
01372 if (_settings_game.order.serviceathelipad) {
01373 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01374
01375 v->date_of_last_service = _date;
01376 v->breakdowns_since_last_service = 0;
01377 v->reliability = v->GetEngine()->reliability;
01378 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01379 }
01380 }
01381 return;
01382 }
01383
01384 if (v->current_order.IsType(OT_NOTHING)) return;
01385
01386
01387 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01388
01389
01390
01391
01392 bool go_to_hangar = false;
01393 switch (v->current_order.GetType()) {
01394 case OT_GOTO_STATION:
01395 break;
01396 case OT_GOTO_DEPOT:
01397 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01398 break;
01399 case OT_CONDITIONAL:
01400
01401
01402
01403 return;
01404 default:
01405 v->current_order.Free();
01406 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01407 }
01408
01409 if (go_to_hangar) {
01410 v->state = HANGAR;
01411 } else {
01412
01413 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01414 }
01415 AirportMove(v, apc);
01416 }
01417
01418 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01419 {
01420 error("OK, you shouldn't be here, check your Airport Scheme!");
01421 }
01422
01423 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01424 {
01425 PlayAircraftSound(v);
01426 v->state = STARTTAKEOFF;
01427 }
01428
01429 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01430 {
01431 v->state = ENDTAKEOFF;
01432 v->UpdateDeltaXY(INVALID_DIR);
01433 }
01434
01435 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01436 {
01437 v->state = FLYING;
01438
01439 AircraftNextAirportPos_and_Order(v);
01440 }
01441
01442 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01443 {
01444 v->state = FLYING;
01445 v->UpdateDeltaXY(INVALID_DIR);
01446
01447
01448 AircraftNextAirportPos_and_Order(v);
01449
01450
01451 if (v->NeedsAutomaticServicing()) {
01452 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01453 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01454 cur_company.Restore();
01455 }
01456 }
01457
01458 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01459 {
01460 Station *st = Station::Get(v->targetairport);
01461
01462
01463 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01464
01465
01466
01467 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01468 const AirportFTA *current = apc->layout[v->pos].next;
01469 while (current != NULL) {
01470 if (current->heading == landingtype) {
01471
01472
01473
01474 uint16 tcur_speed = v->cur_speed;
01475 uint16 tsubspeed = v->subspeed;
01476 if (!AirportHasBlock(v, current, apc)) {
01477 v->state = landingtype;
01478
01479
01480
01481 v->pos = current->next_position;
01482 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01483 return;
01484 }
01485 v->cur_speed = tcur_speed;
01486 v->subspeed = tsubspeed;
01487 }
01488 current = current->next;
01489 }
01490 }
01491 v->state = FLYING;
01492 v->pos = apc->layout[v->pos].next_position;
01493 }
01494
01495 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01496 {
01497 v->state = ENDLANDING;
01498 AircraftLandAirplane(v);
01499
01500
01501 if (v->NeedsAutomaticServicing()) {
01502 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01503 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01504 cur_company.Restore();
01505 }
01506 }
01507
01508 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01509 {
01510 v->state = HELIENDLANDING;
01511 v->UpdateDeltaXY(INVALID_DIR);
01512 }
01513
01514 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01515 {
01516
01517 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01518
01519
01520
01521
01522
01523 if (v->current_order.IsType(OT_GOTO_STATION)) {
01524 if (AirportFindFreeTerminal(v, apc)) return;
01525 }
01526 v->state = HANGAR;
01527
01528 }
01529
01530 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01531 {
01532
01533 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01534
01535
01536
01537
01538
01539
01540
01541
01542 if (v->current_order.IsType(OT_GOTO_STATION)) {
01543 if (AirportFindFreeHelipad(v, apc)) return;
01544 }
01545 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01546 }
01547
01553 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01555 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01556 AircraftEventHandler_General,
01557 AircraftEventHandler_InHangar,
01558 AircraftEventHandler_AtTerminal,
01559 AircraftEventHandler_AtTerminal,
01560 AircraftEventHandler_AtTerminal,
01561 AircraftEventHandler_AtTerminal,
01562 AircraftEventHandler_AtTerminal,
01563 AircraftEventHandler_AtTerminal,
01564 AircraftEventHandler_AtTerminal,
01565 AircraftEventHandler_AtTerminal,
01566 AircraftEventHandler_TakeOff,
01567 AircraftEventHandler_StartTakeOff,
01568 AircraftEventHandler_EndTakeOff,
01569 AircraftEventHandler_HeliTakeOff,
01570 AircraftEventHandler_Flying,
01571 AircraftEventHandler_Landing,
01572 AircraftEventHandler_EndLanding,
01573 AircraftEventHandler_HeliLanding,
01574 AircraftEventHandler_HeliEndLanding,
01575 AircraftEventHandler_AtTerminal,
01576 AircraftEventHandler_AtTerminal,
01577 AircraftEventHandler_AtTerminal,
01578 };
01579
01580 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01581 {
01582
01583 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01584 Station *st = Station::Get(v->targetairport);
01585
01586 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01587 }
01588 }
01589
01590 static void AirportGoToNextPosition(Aircraft *v)
01591 {
01592
01593 if (!AircraftController(v)) return;
01594
01595 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01596
01597 AirportClearBlock(v, apc);
01598 AirportMove(v, apc);
01599 }
01600
01601
01602 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01603 {
01604
01605 if (v->pos >= apc->nofelements) {
01606 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01607 assert(v->pos < apc->nofelements);
01608 }
01609
01610 const AirportFTA *current = &apc->layout[v->pos];
01611
01612 if (current->heading == v->state) {
01613 byte prev_pos = v->pos;
01614 byte prev_state = v->state;
01615 _aircraft_state_handlers[v->state](v, apc);
01616 if (v->state != FLYING) v->previous_pos = prev_pos;
01617 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01618 return true;
01619 }
01620
01621 v->previous_pos = v->pos;
01622
01623
01624 if (current->next == NULL) {
01625 if (AirportSetBlocks(v, current, apc)) {
01626 v->pos = current->next_position;
01627 UpdateAircraftCache(v);
01628 }
01629 return false;
01630 }
01631
01632
01633
01634 do {
01635 if (v->state == current->heading || current->heading == TO_ALL) {
01636 if (AirportSetBlocks(v, current, apc)) {
01637 v->pos = current->next_position;
01638 UpdateAircraftCache(v);
01639 }
01640 return false;
01641 }
01642 current = current->next;
01643 } while (current != NULL);
01644
01645 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01646 NOT_REACHED();
01647 }
01648
01650 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01651 {
01652 const AirportFTA *reference = &apc->layout[v->pos];
01653 const AirportFTA *next = &apc->layout[current_pos->next_position];
01654
01655
01656 if (apc->layout[current_pos->position].block != next->block) {
01657 const Station *st = Station::Get(v->targetairport);
01658 uint64 airport_flags = next->block;
01659
01660
01661 if (current_pos != reference && current_pos->block != NOTHING_block) {
01662 airport_flags |= current_pos->block;
01663 }
01664
01665 if (st->airport.flags & airport_flags) {
01666 v->cur_speed = 0;
01667 v->subspeed = 0;
01668 return true;
01669 }
01670 }
01671 return false;
01672 }
01673
01681 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01682 {
01683 const AirportFTA *next = &apc->layout[current_pos->next_position];
01684 const AirportFTA *reference = &apc->layout[v->pos];
01685
01686
01687 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01688 uint64 airport_flags = next->block;
01689
01690
01691 const AirportFTA *current = current_pos;
01692 if (current == reference) current = current->next;
01693 while (current != NULL) {
01694 if (current->heading == current_pos->heading && current->block != 0) {
01695 airport_flags |= current->block;
01696 break;
01697 }
01698 current = current->next;
01699 }
01700
01701
01702
01703 if (current_pos->block == next->block) airport_flags ^= next->block;
01704
01705 Station *st = Station::Get(v->targetairport);
01706 if (st->airport.flags & airport_flags) {
01707 v->cur_speed = 0;
01708 v->subspeed = 0;
01709 return false;
01710 }
01711
01712 if (next->block != NOTHING_block) {
01713 SETBITS(st->airport.flags, airport_flags);
01714 }
01715 }
01716 return true;
01717 }
01718
01723 struct MovementTerminalMapping {
01724 AirportMovementStates state;
01725 uint64 airport_flag;
01726 };
01727
01729 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01730 {TERM1, TERM1_block},
01731 {TERM2, TERM2_block},
01732 {TERM3, TERM3_block},
01733 {TERM4, TERM4_block},
01734 {TERM5, TERM5_block},
01735 {TERM6, TERM6_block},
01736 {TERM7, TERM7_block},
01737 {TERM8, TERM8_block},
01738 {HELIPAD1, HELIPAD1_block},
01739 {HELIPAD2, HELIPAD2_block},
01740 {HELIPAD3, HELIPAD3_block},
01741 };
01742
01750 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01751 {
01752 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01753 Station *st = Station::Get(v->targetairport);
01754 for (; i < last_terminal; i++) {
01755 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01756
01757 v->state = _airport_terminal_mapping[i].state;
01758 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01759 return true;
01760 }
01761 }
01762 return false;
01763 }
01764
01770 static uint GetNumTerminals(const AirportFTAClass *apc)
01771 {
01772 uint num = 0;
01773
01774 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01775
01776 return num;
01777 }
01778
01785 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01786 {
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797 if (apc->terminals[0] > 1) {
01798 const Station *st = Station::Get(v->targetairport);
01799 const AirportFTA *temp = apc->layout[v->pos].next;
01800
01801 while (temp != NULL) {
01802 if (temp->heading == 255) {
01803 if (!(st->airport.flags & temp->block)) {
01804
01805
01806 uint target_group = temp->next_position + 1;
01807
01808
01809
01810
01811 uint group_start = 0;
01812 for (uint i = 1; i < target_group; i++) {
01813 group_start += apc->terminals[i];
01814 }
01815
01816 uint group_end = group_start + apc->terminals[target_group];
01817 if (FreeTerminal(v, group_start, group_end)) return true;
01818 }
01819 } else {
01820
01821
01822 return false;
01823 }
01824 temp = temp->next;
01825 }
01826 }
01827
01828
01829 return FreeTerminal(v, 0, GetNumTerminals(apc));
01830 }
01831
01838 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01839 {
01840
01841 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01842
01843
01844
01845 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01846 }
01847
01853 static void AircraftHandleDestTooFar(Aircraft *v, bool too_far)
01854 {
01855 if (too_far) {
01856 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01857 SetBit(v->flags, VAF_DEST_TOO_FAR);
01858 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01859 AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index));
01860 if (v->owner == _local_company) {
01861
01862 SetDParam(0, v->index);
01863 AddVehicleNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, NS_ADVICE, v->index);
01864 }
01865 }
01866 return;
01867 }
01868
01869 if (HasBit(v->flags, VAF_DEST_TOO_FAR)) {
01870
01871 ClrBit(v->flags, VAF_DEST_TOO_FAR);
01872 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
01873 DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR);
01874 }
01875 }
01876
01877 static bool AircraftEventHandler(Aircraft *v, int loop)
01878 {
01879 v->tick_counter++;
01880
01881 if (v->vehstatus & VS_CRASHED) {
01882 return HandleCrashedAircraft(v);
01883 }
01884
01885 if (v->vehstatus & VS_STOPPED) return true;
01886
01887 v->HandleBreakdown();
01888
01889 HandleAircraftSmoke(v);
01890 ProcessOrders(v);
01891 v->HandleLoading(loop != 0);
01892
01893 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01894
01895 if (v->state == FLYING) {
01896
01897 AircraftHandleDestTooFar(v, false);
01898 } else if (v->acache.cached_max_range_sqr != 0) {
01899
01900
01901 Station *cur_st = Station::GetIfValid(v->targetairport);
01902 Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : NULL;
01903
01904 if (cur_st != NULL && cur_st->airport.tile != INVALID_TILE && next_st != NULL && next_st->airport.tile != INVALID_TILE) {
01905 uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile);
01906 AircraftHandleDestTooFar(v, dist > v->acache.cached_max_range_sqr);
01907 }
01908 }
01909
01910 if (!HasBit(v->flags, VAF_DEST_TOO_FAR)) AirportGoToNextPosition(v);
01911
01912 return true;
01913 }
01914
01915 bool Aircraft::Tick()
01916 {
01917 if (!this->IsNormalAircraft()) return true;
01918
01919 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01920
01921 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01922
01923 this->current_order_time++;
01924
01925 for (uint i = 0; i != 2; i++) {
01926
01927 if (!AircraftEventHandler(this, i)) return false;
01928 }
01929
01930 return true;
01931 }
01932
01933
01940 Station *GetTargetAirportIfValid(const Aircraft *v)
01941 {
01942 assert(v->type == VEH_AIRCRAFT);
01943
01944 Station *st = Station::GetIfValid(v->targetairport);
01945 if (st == NULL) return NULL;
01946
01947 return st->airport.tile == INVALID_TILE ? NULL : st;
01948 }
01949
01954 void UpdateAirplanesOnNewStation(const Station *st)
01955 {
01956
01957 const AirportFTAClass *ap = st->airport.GetFTA();
01958 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01959
01960 Aircraft *v;
01961 FOR_ALL_AIRCRAFT(v) {
01962 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01963 assert(v->state == FLYING);
01964 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01965 UpdateAircraftCache(v);
01966 }
01967 }