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