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