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