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