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