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