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