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