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
00536 void UpdateAircraftCache(Aircraft *v)
00537 {
00538 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00539 if (max_speed != 0) {
00540
00541 max_speed = (max_speed * 128) / 10;
00542
00543 v->vcache.cached_max_speed = max_speed;
00544 } else {
00545
00546 v->vcache.cached_max_speed = AircraftVehInfo(v->engine_type)->max_speed;
00547 }
00548 }
00549
00550
00554 enum AircraftSpeedLimits {
00555 SPEED_LIMIT_TAXI = 50,
00556 SPEED_LIMIT_APPROACH = 230,
00557 SPEED_LIMIT_BROKEN = 320,
00558 SPEED_LIMIT_HOLD = 425,
00559 SPEED_LIMIT_NONE = 0xFFFF
00560 };
00561
00569 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00570 {
00571 uint spd = v->acceleration * 16;
00572 byte t;
00573
00574
00575
00576 speed_limit *= _settings_game.vehicle.plane_speed;
00577
00578 if (v->vcache.cached_max_speed < speed_limit) {
00579 if (v->cur_speed < speed_limit) hard_limit = false;
00580 speed_limit = v->vcache.cached_max_speed;
00581 }
00582
00583 v->subspeed = (t = v->subspeed) + (byte)spd;
00584
00585
00586
00587
00588
00589
00590
00591 if (!hard_limit && v->cur_speed > speed_limit) {
00592 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00593 }
00594
00595 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00596
00597
00598 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00599
00600
00601 if (spd != v->cur_speed) {
00602 v->cur_speed = spd;
00603 if (_settings_client.gui.vehicle_speed) {
00604 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00605 }
00606 }
00607
00608
00609 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00610
00611
00612 spd = v->GetOldAdvanceSpeed(spd);
00613
00614 spd += v->progress;
00615 v->progress = (byte)spd;
00616 return spd >> 8;
00617 }
00618
00626 byte GetAircraftFlyingAltitude(const Aircraft *v)
00627 {
00628 if (v->subtype == AIR_HELICOPTER) return HELI_FLIGHT_ALTITUDE;
00629
00630
00631
00632
00633 byte base_altitude = PLANE_HOLDING_ALTITUDE;
00634
00635
00636
00637
00638 switch (v->direction) {
00639 case DIR_N:
00640 case DIR_NE:
00641 case DIR_E:
00642 case DIR_SE:
00643 base_altitude += 10;
00644 break;
00645
00646 default: break;
00647 }
00648
00649
00650 base_altitude += min(20 * (v->vcache.cached_max_speed / 200), 90);
00651
00652 return base_altitude;
00653 }
00654
00669 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc, Direction rotation)
00670 {
00671 assert(v != NULL);
00672 assert(apc != NULL);
00673
00674
00675
00676
00677 TileIndex tile = 0;
00678
00679 const Station *st = Station::GetIfValid(v->targetairport);
00680 if (st != NULL) {
00681
00682 tile = (st->airport.tile != INVALID_TILE) ? st->airport.tile : st->xy;
00683 }
00684
00685 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00686 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00687
00688 DiagDirection dir;
00689 if (abs(delta_y) < abs(delta_x)) {
00690
00691 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00692 } else {
00693
00694 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00695 }
00696 dir = ChangeDiagDir(dir, (DiagDirDiff)ReverseDiagDir(DirToDiagDir(rotation)));
00697 return apc->entry_points[dir];
00698 }
00699
00700
00701 static void MaybeCrashAirplane(Aircraft *v);
00702
00710 static bool AircraftController(Aircraft *v)
00711 {
00712 int count;
00713
00714
00715 const Station *st = Station::GetIfValid(v->targetairport);
00716
00717 TileIndex tile = INVALID_TILE;
00718 Direction rotation = DIR_N;
00719 uint size_x = 1, size_y = 1;
00720 if (st != NULL) {
00721 if (st->airport.tile != INVALID_TILE) {
00722 tile = st->airport.tile;
00723 rotation = st->airport.rotation;
00724 size_x = st->airport.w;
00725 size_y = st->airport.h;
00726 } else {
00727 tile = st->xy;
00728 }
00729 }
00730
00731 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
00732
00733
00734 if (st == NULL || st->airport.tile == INVALID_TILE) {
00735
00736 if (v->pos >= afc->nofelements) {
00737 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc, DIR_N);
00738 } else if (v->targetairport != v->current_order.GetDestination()) {
00739
00740 v->state = FLYING;
00741 UpdateAircraftCache(v);
00742 AircraftNextAirportPos_and_Order(v);
00743
00744 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00745 return false;
00746 }
00747 }
00748
00749
00750 const AirportMovingData amd = RotateAirportMovingData(afc->MovingData(v->pos), rotation, size_x, size_y);
00751
00752 int x = TileX(tile) * TILE_SIZE;
00753 int y = TileY(tile) * TILE_SIZE;
00754
00755
00756 if (amd.flag & AMED_HELI_RAISE) {
00757 Aircraft *u = v->Next()->Next();
00758
00759
00760 if (u->cur_speed > 32) {
00761 v->cur_speed = 0;
00762 if (--u->cur_speed == 32) {
00763 if (!PlayVehicleSound(v, VSE_START)) {
00764 SndPlayVehicleFx(SND_18_HELICOPTER, v);
00765 }
00766 }
00767 } else {
00768 u->cur_speed = 32;
00769 count = UpdateAircraftSpeed(v);
00770 if (count > 0) {
00771 v->tile = 0;
00772 byte z_dest = GetAircraftFlyingAltitude(v);
00773
00774
00775 if (v->z_pos >= z_dest) {
00776 v->cur_speed = 0;
00777 return true;
00778 }
00779 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z_dest));
00780 }
00781 }
00782 return false;
00783 }
00784
00785
00786 if (amd.flag & AMED_HELI_LOWER) {
00787 if (st == NULL) {
00788
00789
00790
00791 v->state = FLYING;
00792 UpdateAircraftCache(v);
00793 AircraftNextAirportPos_and_Order(v);
00794 return false;
00795 }
00796
00797
00798 v->tile = tile;
00799
00800
00801 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00802
00803 if (z == v->z_pos) {
00804 Vehicle *u = v->Next()->Next();
00805
00806
00807 if (u->cur_speed >= 80) return true;
00808 u->cur_speed += 4;
00809 } else {
00810 count = UpdateAircraftSpeed(v);
00811 if (count > 0) {
00812 if (v->z_pos > z) {
00813 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00814 } else {
00815 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00816 }
00817 }
00818 }
00819 return false;
00820 }
00821
00822
00823 uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos);
00824
00825
00826 if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00827
00828
00829 if (dist == 0) {
00830
00831 DirDiff dirdiff = DirDifference(amd.direction, v->direction);
00832
00833
00834 if (dirdiff == DIRDIFF_SAME) {
00835 v->cur_speed = 0;
00836 return true;
00837 }
00838
00839 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00840
00841 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00842 v->cur_speed >>= 1;
00843
00844 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00845 return false;
00846 }
00847
00848 if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00849 MaybeCrashAirplane(v);
00850 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00851 }
00852
00853 uint speed_limit = SPEED_LIMIT_TAXI;
00854 bool hard_limit = true;
00855
00856 if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00857 if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00858 if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00859 if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00860
00861 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00862 if (count == 0) return false;
00863
00864 if (v->turn_counter != 0) v->turn_counter--;
00865
00866 do {
00867
00868 GetNewVehiclePosResult gp;
00869
00870 if (dist < 4 || (amd.flag & AMED_LAND)) {
00871
00872 gp.x = (v->x_pos != (x + amd.x)) ?
00873 v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) :
00874 v->x_pos;
00875 gp.y = (v->y_pos != (y + amd.y)) ?
00876 v->y_pos + ((y + amd.y > v->y_pos) ? 1 : -1) :
00877 v->y_pos;
00878
00879
00880 gp.new_tile = (st->airport.type == AT_OILRIG) ? st->airport.tile : TileVirtXY(gp.x, gp.y);
00881
00882 } else {
00883
00884
00885 Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y);
00886 if (newdir != v->direction) {
00887 if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
00888 if (v->turn_counter == 0 || newdir == v->last_direction) {
00889 if (newdir == v->last_direction) {
00890 v->number_consecutive_turns = 0;
00891 } else {
00892 v->number_consecutive_turns++;
00893 }
00894 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
00895 v->last_direction = v->direction;
00896 v->direction = newdir;
00897 }
00898
00899
00900 gp = GetNewVehiclePos(v);
00901 } else {
00902 v->cur_speed >>= 1;
00903 v->direction = newdir;
00904
00905
00906
00907
00908
00909
00910 gp.x = v->x_pos;
00911 gp.y = v->y_pos;
00912 gp.new_tile = gp.old_tile = v->tile;
00913 }
00914 } else {
00915 v->number_consecutive_turns = 0;
00916
00917 gp = GetNewVehiclePos(v);
00918 }
00919 }
00920
00921 v->tile = gp.new_tile;
00922
00923 if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
00924
00925
00926 uint z = v->z_pos;
00927
00928 if (amd.flag & AMED_TAKEOFF) {
00929 z = min(z + 2, GetAircraftFlyingAltitude(v));
00930 }
00931
00932
00933 if ((amd.flag & AMED_HOLD) && (z > PLANE_HOLDING_ALTITUDE)) z--;
00934
00935 if (amd.flag & AMED_LAND) {
00936 if (st->airport.tile == INVALID_TILE) {
00937
00938 v->state = FLYING;
00939 UpdateAircraftCache(v);
00940 AircraftNextAirportPos_and_Order(v);
00941
00942 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
00943 continue;
00944 }
00945
00946 uint curz = GetSlopeZ(x + amd.x, y + amd.y) + 1;
00947
00948
00949 assert(curz <= z);
00950 int t = max(1U, dist - 4);
00951 int delta = z - curz;
00952
00953
00954 if (delta >= t) {
00955 z -= CeilDiv(z - curz, t);
00956 }
00957 if (z < curz) z = curz;
00958 }
00959
00960
00961 if (amd.flag & AMED_BRAKE) {
00962 uint curz = GetSlopeZ(x, y) + 1;
00963
00964 if (z > curz) {
00965 z--;
00966 } else if (z < curz) {
00967 z++;
00968 }
00969
00970 }
00971
00972 SetAircraftPosition(v, gp.x, gp.y, z);
00973 } while (--count != 0);
00974 return false;
00975 }
00976
00981 static bool HandleCrashedAircraft(Aircraft *v)
00982 {
00983 v->crashed_counter += 3;
00984
00985 Station *st = GetTargetAirportIfValid(v);
00986
00987
00988 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
00989 uint z = GetSlopeZ(v->x_pos, v->y_pos);
00990 v->z_pos -= 1;
00991 if (v->z_pos == z) {
00992 v->crashed_counter = 500;
00993 v->z_pos++;
00994 }
00995 }
00996
00997 if (v->crashed_counter < 650) {
00998 uint32 r;
00999 if (Chance16R(1, 32, r)) {
01000 static const DirDiff delta[] = {
01001 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01002 };
01003
01004 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01005 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01006 r = Random();
01007 CreateEffectVehicleRel(v,
01008 GB(r, 0, 4) - 4,
01009 GB(r, 4, 4) - 4,
01010 GB(r, 8, 4),
01011 EV_EXPLOSION_SMALL);
01012 }
01013 } else if (v->crashed_counter >= 10000) {
01014
01015
01016
01017
01018
01019 if (st != NULL) {
01020 CLRBITS(st->airport.flags, RUNWAY_IN_block);
01021 CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block);
01022 CLRBITS(st->airport.flags, RUNWAY_IN2_block);
01023 }
01024
01025 delete v;
01026
01027 return false;
01028 }
01029
01030 return true;
01031 }
01032
01033
01034 static void HandleAircraftSmoke(Aircraft *v)
01035 {
01036 static const struct {
01037 int8 x;
01038 int8 y;
01039 } smoke_pos[] = {
01040 { 5, 5 },
01041 { 6, 0 },
01042 { 5, -5 },
01043 { 0, -6 },
01044 { -5, -5 },
01045 { -6, 0 },
01046 { -5, 5 },
01047 { 0, 6 }
01048 };
01049
01050 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01051
01052 if (v->cur_speed < 10) {
01053 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01054 v->breakdown_ctr = 0;
01055 return;
01056 }
01057
01058 if ((v->tick_counter & 0x1F) == 0) {
01059 CreateEffectVehicleRel(v,
01060 smoke_pos[v->direction].x,
01061 smoke_pos[v->direction].y,
01062 2,
01063 EV_SMOKE
01064 );
01065 }
01066 }
01067
01068 void HandleMissingAircraftOrders(Aircraft *v)
01069 {
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085 const Station *st = GetTargetAirportIfValid(v);
01086 if (st == NULL) {
01087 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01088 CommandCost ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01089 cur_company.Restore();
01090
01091 if (ret.Failed()) CrashAirplane(v);
01092 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01093 v->current_order.Free();
01094 }
01095 }
01096
01097
01098 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01099 {
01100
01101 if (this->state == FLYING) {
01102 AircraftNextAirportPos_and_Order(this);
01103 }
01104
01105
01106 return 0;
01107 }
01108
01109 void Aircraft::MarkDirty()
01110 {
01111 this->UpdateViewport(false, false);
01112 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01113 }
01114
01115
01116 uint Aircraft::Crash(bool flooded)
01117 {
01118 uint pass = Vehicle::Crash(flooded) + 2;
01119 this->crashed_counter = flooded ? 9000 : 0;
01120
01121 return pass;
01122 }
01123
01128 static void CrashAirplane(Aircraft *v)
01129 {
01130 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01131
01132 uint pass = v->Crash();
01133 SetDParam(0, pass);
01134
01135 v->cargo.Truncate(0);
01136 v->Next()->cargo.Truncate(0);
01137 const Station *st = GetTargetAirportIfValid(v);
01138 StringID newsitem;
01139 if (st == NULL) {
01140 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01141 } else {
01142 SetDParam(1, st->index);
01143 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01144 }
01145
01146 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, st == NULL ? AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : AIEventVehicleCrashed::CRASH_PLANE_LANDING));
01147
01148 AddVehicleNewsItem(newsitem,
01149 NS_ACCIDENT,
01150 v->index,
01151 st != NULL ? st->index : INVALID_STATION);
01152
01153 ModifyStationRatingAround(v->tile, v->owner, -160, 30);
01154 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01155 }
01156
01161 static void MaybeCrashAirplane(Aircraft *v)
01162 {
01163 if (_settings_game.vehicle.plane_crashes == 0) return;
01164
01165 Station *st = Station::Get(v->targetairport);
01166
01167
01168 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01169 if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
01170 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01171 !_cheats.no_jetcrash.value) {
01172 prob /= 20;
01173 } else {
01174 prob /= 1500;
01175 }
01176
01177 if (GB(Random(), 0, 22) > prob) return;
01178
01179
01180 for (CargoID i = 0; i < NUM_CARGO; i++) {
01181 st->goods[i].rating = 1;
01182 st->goods[i].cargo.Truncate(0);
01183 }
01184
01185 CrashAirplane(v);
01186 }
01187
01193 static void AircraftEntersTerminal(Aircraft *v)
01194 {
01195 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01196
01197 Station *st = Station::Get(v->targetairport);
01198 v->last_station_visited = v->targetairport;
01199
01200
01201 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01202 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01203 SetDParam(0, st->index);
01204
01205 AddVehicleNewsItem(
01206 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01207 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01208 v->index,
01209 st->index
01210 );
01211 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01212 }
01213
01214 v->BeginLoading();
01215 }
01216
01221 static void AircraftLandAirplane(Aircraft *v)
01222 {
01223 v->UpdateDeltaXY(INVALID_DIR);
01224
01225 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01226 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01227 }
01228 }
01229
01230
01232 void AircraftNextAirportPos_and_Order(Aircraft *v)
01233 {
01234 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01235 v->targetairport = v->current_order.GetDestination();
01236 }
01237
01238 const Station *st = GetTargetAirportIfValid(v);
01239 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->airport.GetFTA();
01240 Direction rotation = st == NULL ? DIR_N : st->airport.rotation;
01241 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc, rotation);
01242 }
01243
01244 void AircraftLeaveHangar(Aircraft *v)
01245 {
01246 v->cur_speed = 0;
01247 v->subspeed = 0;
01248 v->progress = 0;
01249 v->direction = DIR_SE;
01250 v->vehstatus &= ~VS_HIDDEN;
01251 {
01252 Vehicle *u = v->Next();
01253 u->vehstatus &= ~VS_HIDDEN;
01254
01255
01256 u = u->Next();
01257 if (u != NULL) {
01258 u->vehstatus &= ~VS_HIDDEN;
01259 u->cur_speed = 80;
01260 }
01261 }
01262
01263 VehicleServiceInDepot(v);
01264 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01265 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01266 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01267 }
01268
01272 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01273 {
01274 AircraftEntersTerminal(v);
01275 v->state = apc->layout[v->pos].heading;
01276 }
01277
01283 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01284 {
01285 VehicleEnterDepot(v);
01286 v->state = apc->layout[v->pos].heading;
01287 }
01288
01294 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01295 {
01296
01297 if (v->previous_pos != v->pos) {
01298 AircraftEventHandler_EnterHangar(v, apc);
01299 return;
01300 }
01301
01302
01303 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01304 v->current_order.Free();
01305 return;
01306 }
01307
01308 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01309 !v->current_order.IsType(OT_GOTO_DEPOT))
01310 return;
01311
01312
01313 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01314
01315
01316 if (v->current_order.GetDestination() == v->targetairport) {
01317
01318
01319 if (v->subtype == AIR_HELICOPTER) {
01320 if (!AirportFindFreeHelipad(v, apc)) return;
01321 } else {
01322 if (!AirportFindFreeTerminal(v, apc)) return;
01323 }
01324 } else {
01325
01326 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01327 }
01328 AircraftLeaveHangar(v);
01329 AirportMove(v, apc);
01330 }
01331
01333 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01334 {
01335
01336 if (v->previous_pos != v->pos) {
01337 AircraftEventHandler_EnterTerminal(v, apc);
01338
01339
01340 if (_settings_game.order.serviceathelipad) {
01341 if (v->subtype == AIR_HELICOPTER && apc->num_helipads > 0) {
01342
01343 v->date_of_last_service = _date;
01344 v->breakdowns_since_last_service = 0;
01345 v->reliability = Engine::Get(v->engine_type)->reliability;
01346 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01347 }
01348 }
01349 return;
01350 }
01351
01352 if (v->current_order.IsType(OT_NOTHING)) return;
01353
01354
01355 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01356
01357
01358
01359
01360 bool go_to_hangar = false;
01361 switch (v->current_order.GetType()) {
01362 case OT_GOTO_STATION:
01363 break;
01364 case OT_GOTO_DEPOT:
01365 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01366 break;
01367 case OT_CONDITIONAL:
01368
01369
01370
01371 return;
01372 default:
01373 v->current_order.Free();
01374 go_to_hangar = Station::Get(v->targetairport)->airport.HasHangar();
01375 }
01376
01377 if (go_to_hangar) {
01378 v->state = HANGAR;
01379 } else {
01380
01381 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01382 }
01383 AirportMove(v, apc);
01384 }
01385
01386 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01387 {
01388 error("OK, you shouldn't be here, check your Airport Scheme!");
01389 }
01390
01391 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01392 {
01393 PlayAircraftSound(v);
01394 v->state = STARTTAKEOFF;
01395 }
01396
01397 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01398 {
01399 v->state = ENDTAKEOFF;
01400 v->UpdateDeltaXY(INVALID_DIR);
01401 }
01402
01403 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01404 {
01405 v->state = FLYING;
01406
01407 AircraftNextAirportPos_and_Order(v);
01408 }
01409
01410 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01411 {
01412 v->state = FLYING;
01413 v->UpdateDeltaXY(INVALID_DIR);
01414
01415
01416 AircraftNextAirportPos_and_Order(v);
01417
01418
01419 if (v->NeedsAutomaticServicing()) {
01420 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01421 DoCommand(v->tile, v->index | DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01422 cur_company.Restore();
01423 }
01424 }
01425
01426 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01427 {
01428 Station *st = Station::Get(v->targetairport);
01429
01430
01431 if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner)) {
01432
01433
01434
01435 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01436 const AirportFTA *current = apc->layout[v->pos].next;
01437 while (current != NULL) {
01438 if (current->heading == landingtype) {
01439
01440
01441
01442 uint16 tcur_speed = v->cur_speed;
01443 uint16 tsubspeed = v->subspeed;
01444 if (!AirportHasBlock(v, current, apc)) {
01445 v->state = landingtype;
01446
01447
01448
01449 v->pos = current->next_position;
01450 SETBITS(st->airport.flags, apc->layout[v->pos].block);
01451 return;
01452 }
01453 v->cur_speed = tcur_speed;
01454 v->subspeed = tsubspeed;
01455 }
01456 current = current->next;
01457 }
01458 }
01459 v->state = FLYING;
01460 v->pos = apc->layout[v->pos].next_position;
01461 }
01462
01463 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01464 {
01465 v->state = ENDLANDING;
01466 AircraftLandAirplane(v);
01467
01468
01469 if (v->NeedsAutomaticServicing()) {
01470 Backup<CompanyByte> cur_company(_current_company, v->owner, FILE_LINE);
01471 DoCommand(v->tile, v->index | DEPOT_SERVICE, 0, DC_EXEC, CMD_SEND_VEHICLE_TO_DEPOT);
01472 cur_company.Restore();
01473 }
01474 }
01475
01476 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01477 {
01478 v->state = HELIENDLANDING;
01479 v->UpdateDeltaXY(INVALID_DIR);
01480 }
01481
01482 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01483 {
01484
01485 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01486
01487
01488
01489
01490
01491 if (v->current_order.IsType(OT_GOTO_STATION)) {
01492 if (AirportFindFreeTerminal(v, apc)) return;
01493 }
01494 v->state = HANGAR;
01495
01496 }
01497
01498 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01499 {
01500
01501 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01502
01503
01504
01505
01506
01507
01508
01509
01510 if (v->current_order.IsType(OT_GOTO_STATION)) {
01511 if (AirportFindFreeHelipad(v, apc)) return;
01512 }
01513 v->state = Station::Get(v->targetairport)->airport.HasHangar() ? HANGAR : HELITAKEOFF;
01514 }
01515
01521 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01523 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01524 AircraftEventHandler_General,
01525 AircraftEventHandler_InHangar,
01526 AircraftEventHandler_AtTerminal,
01527 AircraftEventHandler_AtTerminal,
01528 AircraftEventHandler_AtTerminal,
01529 AircraftEventHandler_AtTerminal,
01530 AircraftEventHandler_AtTerminal,
01531 AircraftEventHandler_AtTerminal,
01532 AircraftEventHandler_AtTerminal,
01533 AircraftEventHandler_AtTerminal,
01534 AircraftEventHandler_TakeOff,
01535 AircraftEventHandler_StartTakeOff,
01536 AircraftEventHandler_EndTakeOff,
01537 AircraftEventHandler_HeliTakeOff,
01538 AircraftEventHandler_Flying,
01539 AircraftEventHandler_Landing,
01540 AircraftEventHandler_EndLanding,
01541 AircraftEventHandler_HeliLanding,
01542 AircraftEventHandler_HeliEndLanding,
01543 AircraftEventHandler_AtTerminal,
01544 AircraftEventHandler_AtTerminal,
01545 AircraftEventHandler_AtTerminal,
01546 };
01547
01548 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01549 {
01550
01551 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01552 Station *st = Station::Get(v->targetairport);
01553
01554 CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block);
01555 }
01556 }
01557
01558 static void AirportGoToNextPosition(Aircraft *v)
01559 {
01560
01561 if (!AircraftController(v)) return;
01562
01563 const AirportFTAClass *apc = Station::Get(v->targetairport)->airport.GetFTA();
01564
01565 AirportClearBlock(v, apc);
01566 AirportMove(v, apc);
01567 }
01568
01569
01570 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01571 {
01572
01573 if (v->pos >= apc->nofelements) {
01574 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01575 assert(v->pos < apc->nofelements);
01576 }
01577
01578 const AirportFTA *current = &apc->layout[v->pos];
01579
01580 if (current->heading == v->state) {
01581 byte prev_pos = v->pos;
01582 byte prev_state = v->state;
01583 _aircraft_state_handlers[v->state](v, apc);
01584 if (v->state != FLYING) v->previous_pos = prev_pos;
01585 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01586 return true;
01587 }
01588
01589 v->previous_pos = v->pos;
01590
01591
01592 if (current->next == NULL) {
01593 if (AirportSetBlocks(v, current, apc)) {
01594 v->pos = current->next_position;
01595 UpdateAircraftCache(v);
01596 }
01597 return false;
01598 }
01599
01600
01601
01602 do {
01603 if (v->state == current->heading || current->heading == TO_ALL) {
01604 if (AirportSetBlocks(v, current, apc)) {
01605 v->pos = current->next_position;
01606 UpdateAircraftCache(v);
01607 }
01608 return false;
01609 }
01610 current = current->next;
01611 } while (current != NULL);
01612
01613 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01614 NOT_REACHED();
01615 }
01616
01618 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01619 {
01620 const AirportFTA *reference = &apc->layout[v->pos];
01621 const AirportFTA *next = &apc->layout[current_pos->next_position];
01622
01623
01624 if (apc->layout[current_pos->position].block != next->block) {
01625 const Station *st = Station::Get(v->targetairport);
01626 uint64 airport_flags = next->block;
01627
01628
01629 if (current_pos != reference && current_pos->block != NOTHING_block) {
01630 airport_flags |= current_pos->block;
01631 }
01632
01633 if (st->airport.flags & airport_flags) {
01634 v->cur_speed = 0;
01635 v->subspeed = 0;
01636 return true;
01637 }
01638 }
01639 return false;
01640 }
01641
01649 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01650 {
01651 const AirportFTA *next = &apc->layout[current_pos->next_position];
01652 const AirportFTA *reference = &apc->layout[v->pos];
01653
01654
01655 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01656 uint64 airport_flags = next->block;
01657
01658
01659 const AirportFTA *current = current_pos;
01660 if (current == reference) current = current->next;
01661 while (current != NULL) {
01662 if (current->heading == current_pos->heading && current->block != 0) {
01663 airport_flags |= current->block;
01664 break;
01665 }
01666 current = current->next;
01667 };
01668
01669
01670
01671 if (current_pos->block == next->block) airport_flags ^= next->block;
01672
01673 Station *st = Station::Get(v->targetairport);
01674 if (st->airport.flags & airport_flags) {
01675 v->cur_speed = 0;
01676 v->subspeed = 0;
01677 return false;
01678 }
01679
01680 if (next->block != NOTHING_block) {
01681 SETBITS(st->airport.flags, airport_flags);
01682 }
01683 }
01684 return true;
01685 }
01686
01691 struct MovementTerminalMapping {
01692 AirportMovementStates state;
01693 uint64 airport_flag;
01694 };
01695
01697 static const MovementTerminalMapping _airport_terminal_mapping[] = {
01698 {TERM1, TERM1_block},
01699 {TERM2, TERM2_block},
01700 {TERM3, TERM3_block},
01701 {TERM4, TERM4_block},
01702 {TERM5, TERM5_block},
01703 {TERM6, TERM6_block},
01704 {TERM7, TERM7_block},
01705 {TERM8, TERM8_block},
01706 {HELIPAD1, HELIPAD1_block},
01707 {HELIPAD2, HELIPAD2_block},
01708 {HELIPAD3, HELIPAD3_block},
01709 };
01710
01718 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01719 {
01720 assert(last_terminal <= lengthof(_airport_terminal_mapping));
01721 Station *st = Station::Get(v->targetairport);
01722 for (; i < last_terminal; i++) {
01723 if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) {
01724
01725 v->state = _airport_terminal_mapping[i].state;
01726 SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag);
01727 return true;
01728 }
01729 }
01730 return false;
01731 }
01732
01738 static uint GetNumTerminals(const AirportFTAClass *apc)
01739 {
01740 uint num = 0;
01741
01742 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01743
01744 return num;
01745 }
01746
01753 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01754 {
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765 if (apc->terminals[0] > 1) {
01766 const Station *st = Station::Get(v->targetairport);
01767 const AirportFTA *temp = apc->layout[v->pos].next;
01768
01769 while (temp != NULL) {
01770 if (temp->heading == 255) {
01771 if (!(st->airport.flags & temp->block)) {
01772
01773
01774 uint target_group = temp->next_position + 1;
01775
01776
01777
01778
01779 uint group_start = 0;
01780 for (uint i = 1; i < target_group; i++) {
01781 group_start += apc->terminals[i];
01782 }
01783
01784 uint group_end = group_start + apc->terminals[target_group];
01785 if (FreeTerminal(v, group_start, group_end)) return true;
01786 }
01787 } else {
01788
01789
01790 return false;
01791 }
01792 temp = temp->next;
01793 }
01794 }
01795
01796
01797 return FreeTerminal(v, 0, GetNumTerminals(apc));
01798 }
01799
01806 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01807 {
01808
01809 if (apc->num_helipads == 0) return AirportFindFreeTerminal(v, apc);
01810
01811
01812
01813 return FreeTerminal(v, MAX_TERMINALS, apc->num_helipads + MAX_TERMINALS);
01814 }
01815
01816 static bool AircraftEventHandler(Aircraft *v, int loop)
01817 {
01818 v->tick_counter++;
01819
01820 if (v->vehstatus & VS_CRASHED) {
01821 return HandleCrashedAircraft(v);
01822 }
01823
01824 if (v->vehstatus & VS_STOPPED) return true;
01825
01826 v->HandleBreakdown();
01827
01828 HandleAircraftSmoke(v);
01829 ProcessOrders(v);
01830 v->HandleLoading(loop != 0);
01831
01832 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01833
01834 AirportGoToNextPosition(v);
01835
01836 return true;
01837 }
01838
01839 bool Aircraft::Tick()
01840 {
01841 if (!this->IsNormalAircraft()) return true;
01842
01843 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01844
01845 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01846
01847 this->current_order_time++;
01848
01849 for (uint i = 0; i != 2; i++) {
01850
01851 if (!AircraftEventHandler(this, i)) return false;
01852 }
01853
01854 return true;
01855 }
01856
01857
01864 Station *GetTargetAirportIfValid(const Aircraft *v)
01865 {
01866 assert(v->type == VEH_AIRCRAFT);
01867
01868 Station *st = Station::GetIfValid(v->targetairport);
01869 if (st == NULL) return NULL;
01870
01871 return st->airport.tile == INVALID_TILE ? NULL : st;
01872 }
01873
01878 void UpdateAirplanesOnNewStation(const Station *st)
01879 {
01880
01881 const AirportFTAClass *ap = st->airport.GetFTA();
01882 Direction rotation = st->airport.tile == INVALID_TILE ? DIR_N : st->airport.rotation;
01883
01884 Aircraft *v;
01885 FOR_ALL_AIRCRAFT(v) {
01886 if (!v->IsNormalAircraft() || v->targetairport != st->index) continue;
01887 assert(v->state == FLYING);
01888 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap, rotation);
01889 UpdateAircraftCache(v);
01890 }
01891 }