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