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 "company_base.h"
00031 #include "autoreplace_gui.h"
00032 #include "ai/ai.hpp"
00033 #include "company_func.h"
00034 #include "effectvehicle_func.h"
00035 #include "station_base.h"
00036 #include "engine_base.h"
00037 #include "engine_func.h"
00038 #include "core/random_func.hpp"
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 (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
00118
00119 const AirportFTAClass *afc = st->Airport();
00120 const AirportSpec *as = st->GetAirportSpec();
00121 if (as->nof_depots == 0 || (
00122
00123 (afc->flags & AirportFTAClass::SHORT_STRIP) &&
00124 (avi->subtype & AIR_FAST) &&
00125 !_cheats.no_jetcrash.value)) {
00126 continue;
00127 }
00128
00129
00130 uint distance = DistanceSquare(vtile, st->airport_tile);
00131 if (distance < best || index == INVALID_STATION) {
00132 best = distance;
00133 index = st->index;
00134 }
00135 }
00136 return index;
00137 }
00138
00139 #if 0
00140
00143 static bool HaveHangarInOrderList(Aircraft *v)
00144 {
00145 const Order *order;
00146
00147 FOR_VEHICLE_ORDERS(v, order) {
00148 const Station *st = Station::Get(order->station);
00149 if (st->owner == v->owner && (st->facilities & FACIL_AIRPORT)) {
00150
00151 if (st->Airport()->nof_depots != 0)
00152 return true;
00153 }
00154 }
00155
00156 return false;
00157 }
00158 #endif
00159
00160 SpriteID Aircraft::GetImage(Direction direction) const
00161 {
00162 uint8 spritenum = this->spritenum;
00163
00164 if (is_custom_sprite(spritenum)) {
00165 SpriteID sprite = GetCustomVehicleSprite(this, direction);
00166 if (sprite != 0) return sprite;
00167
00168 spritenum = Engine::Get(this->engine_type)->original_image_index;
00169 }
00170
00171 return direction + _aircraft_sprite[spritenum];
00172 }
00173
00174 SpriteID GetRotorImage(const Aircraft *v)
00175 {
00176 assert(v->subtype == AIR_HELICOPTER);
00177
00178 const Aircraft *w = v->Next()->Next();
00179 if (is_custom_sprite(v->spritenum)) {
00180 SpriteID sprite = GetCustomRotorSprite(v, false);
00181 if (sprite != 0) return sprite;
00182 }
00183
00184
00185 return SPR_ROTOR_STOPPED + w->state;
00186 }
00187
00188 static SpriteID GetAircraftIcon(EngineID engine)
00189 {
00190 const Engine *e = Engine::Get(engine);
00191 uint8 spritenum = e->u.air.image_index;
00192
00193 if (is_custom_sprite(spritenum)) {
00194 SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
00195 if (sprite != 0) return sprite;
00196
00197 spritenum = e->original_image_index;
00198 }
00199
00200 return DIR_W + _aircraft_sprite[spritenum];
00201 }
00202
00203 void DrawAircraftEngine(int left, int right, int preferred_x, int y, EngineID engine, SpriteID pal)
00204 {
00205 SpriteID sprite = GetAircraftIcon(engine);
00206 const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
00207 preferred_x = Clamp(preferred_x, left - real_sprite->x_offs, right - real_sprite->width - real_sprite->x_offs);
00208 DrawSprite(sprite, pal, preferred_x, y);
00209
00210 if (!(AircraftVehInfo(engine)->subtype & AIR_CTOL)) {
00211 SpriteID rotor_sprite = GetCustomRotorIcon(engine);
00212 if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED;
00213 DrawSprite(rotor_sprite, PAL_NONE, preferred_x, y - 5);
00214 }
00215 }
00216
00222 void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height)
00223 {
00224 const Sprite *spr = GetSprite(GetAircraftIcon(engine), ST_NORMAL);
00225
00226 width = spr->width;
00227 height = spr->height;
00228 }
00229
00238 CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00239 {
00240 if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_company)) return_cmd_error(STR_ERROR_AIRCRAFT_NOT_AVAILABLE);
00241
00242 const Engine *e = Engine::Get(p1);
00243 const AircraftVehicleInfo *avi = &e->u.air;
00244 CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
00245
00246
00247 if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00248
00249
00250 if (flags & DC_QUERY_COST) return value;
00251
00252 if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
00253
00254
00255 if (!CanVehicleUseStation(p1, Station::GetByTile(tile))) return CMD_ERROR;
00256
00257
00258 if (!Vehicle::CanAllocateItem(avi->subtype & AIR_CTOL ? 2 : 3)) {
00259 return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
00260 }
00261
00262 UnitID unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
00263 if (unit_num > _settings_game.vehicle.max_aircraft)
00264 return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
00265
00266 if (flags & DC_EXEC) {
00267 Aircraft *v = new Aircraft();
00268 Aircraft *u = new Aircraft();
00269
00270 v->unitnumber = unit_num;
00271 v->direction = DIR_SE;
00272
00273 v->owner = u->owner = _current_company;
00274
00275 v->tile = tile;
00276
00277
00278 uint x = TileX(tile) * TILE_SIZE + 5;
00279 uint y = TileY(tile) * TILE_SIZE + 3;
00280
00281 v->x_pos = u->x_pos = x;
00282 v->y_pos = u->y_pos = y;
00283
00284 u->z_pos = GetSlopeZ(x, y);
00285 v->z_pos = u->z_pos + 1;
00286
00287 v->running_ticks = 0;
00288
00289
00290
00291 v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00292 u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
00293
00294 v->spritenum = avi->image_index;
00295
00296
00297 v->cargo_cap = avi->passenger_capacity;
00298 u->cargo_cap = avi->mail_capacity;
00299
00300 v->cargo_type = e->GetDefaultCargoType();
00301 u->cargo_type = CT_MAIL;
00302
00303 v->cargo_subtype = 0;
00304
00305 v->name = NULL;
00306
00307
00308
00309
00310 v->last_station_visited = INVALID_STATION;
00311
00312
00313 v->max_speed = avi->max_speed;
00314 v->acceleration = avi->acceleration;
00315 v->engine_type = p1;
00316 u->engine_type = p1;
00317
00318 v->subtype = (avi->subtype & AIR_CTOL ? AIR_AIRCRAFT : AIR_HELICOPTER);
00319 v->UpdateDeltaXY(INVALID_DIR);
00320 v->value = value.GetCost();
00321
00322 u->subtype = AIR_SHADOW;
00323 u->UpdateDeltaXY(INVALID_DIR);
00324
00325 v->reliability = e->reliability;
00326 v->reliability_spd_dec = e->reliability_spd_dec;
00327 v->max_age = e->GetLifeLengthInDays();
00328
00329 _new_vehicle_id = v->index;
00330
00331
00332
00333
00334
00335 for (uint i = 0;; i++) {
00336 const Station *st = Station::GetByTile(tile);
00337 const AirportFTAClass *apc = st->Airport();
00338
00339 if (st->GetHangarTile(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->GetAirportSpec()->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->turn_counter != 0) v->turn_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->turn_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->turn_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 (ret.Failed()) 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)->GetAirportSpec()->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 (st->owner == OWNER_NONE || st->owner == v->owner)) {
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 const AirportSpec *as = Station::Get(v->targetairport)->GetAirportSpec();
01620 v->state = (as->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01621 }
01622
01623 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01624 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01625 AircraftEventHandler_General,
01626 AircraftEventHandler_InHangar,
01627 AircraftEventHandler_AtTerminal,
01628 AircraftEventHandler_AtTerminal,
01629 AircraftEventHandler_AtTerminal,
01630 AircraftEventHandler_AtTerminal,
01631 AircraftEventHandler_AtTerminal,
01632 AircraftEventHandler_AtTerminal,
01633 AircraftEventHandler_AtTerminal,
01634 AircraftEventHandler_AtTerminal,
01635 AircraftEventHandler_TakeOff,
01636 AircraftEventHandler_StartTakeOff,
01637 AircraftEventHandler_EndTakeOff,
01638 AircraftEventHandler_HeliTakeOff,
01639 AircraftEventHandler_Flying,
01640 AircraftEventHandler_Landing,
01641 AircraftEventHandler_EndLanding,
01642 AircraftEventHandler_HeliLanding,
01643 AircraftEventHandler_HeliEndLanding,
01644 AircraftEventHandler_AtTerminal,
01645 AircraftEventHandler_AtTerminal,
01646 AircraftEventHandler_AtTerminal,
01647 AircraftEventHandler_AtTerminal,
01648 };
01649
01650 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01651 {
01652
01653 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01654 Station *st = Station::Get(v->targetairport);
01655
01656 CLRBITS(st->airport_flags, apc->layout[v->previous_pos].block);
01657 }
01658 }
01659
01660 static void AirportGoToNextPosition(Aircraft *v)
01661 {
01662
01663 if (!AircraftController(v)) return;
01664
01665 const AirportFTAClass *apc = Station::Get(v->targetairport)->Airport();
01666
01667 AirportClearBlock(v, apc);
01668 AirportMove(v, apc);
01669 }
01670
01671
01672 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01673 {
01674
01675 if (v->pos >= apc->nofelements) {
01676 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01677 assert(v->pos < apc->nofelements);
01678 }
01679
01680 const AirportFTA *current = &apc->layout[v->pos];
01681
01682 if (current->heading == v->state) {
01683 byte prev_pos = v->pos;
01684 byte prev_state = v->state;
01685 _aircraft_state_handlers[v->state](v, apc);
01686 if (v->state != FLYING) v->previous_pos = prev_pos;
01687 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01688 return true;
01689 }
01690
01691 v->previous_pos = v->pos;
01692
01693
01694 if (current->next == NULL) {
01695 if (AirportSetBlocks(v, current, apc)) {
01696 v->pos = current->next_position;
01697 UpdateAircraftCache(v);
01698 }
01699 return false;
01700 }
01701
01702
01703
01704 do {
01705 if (v->state == current->heading || current->heading == TO_ALL) {
01706 if (AirportSetBlocks(v, current, apc)) {
01707 v->pos = current->next_position;
01708 UpdateAircraftCache(v);
01709 }
01710 return false;
01711 }
01712 current = current->next;
01713 } while (current != NULL);
01714
01715 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01716 NOT_REACHED();
01717 }
01718
01719
01720 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01721 {
01722 const AirportFTA *reference = &apc->layout[v->pos];
01723 const AirportFTA *next = &apc->layout[current_pos->next_position];
01724
01725
01726 if (apc->layout[current_pos->position].block != next->block) {
01727 const Station *st = Station::Get(v->targetairport);
01728 uint64 airport_flags = next->block;
01729
01730
01731 if (current_pos != reference && current_pos->block != NOTHING_block) {
01732 airport_flags |= current_pos->block;
01733 }
01734
01735 if (st->airport_flags & airport_flags) {
01736 v->cur_speed = 0;
01737 v->subspeed = 0;
01738 return true;
01739 }
01740 }
01741 return false;
01742 }
01743
01751 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01752 {
01753 const AirportFTA *next = &apc->layout[current_pos->next_position];
01754 const AirportFTA *reference = &apc->layout[v->pos];
01755
01756
01757 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01758 uint64 airport_flags = next->block;
01759
01760
01761 const AirportFTA *current = current_pos;
01762 if (current == reference) current = current->next;
01763 while (current != NULL) {
01764 if (current->heading == current_pos->heading && current->block != 0) {
01765 airport_flags |= current->block;
01766 break;
01767 }
01768 current = current->next;
01769 };
01770
01771
01772
01773 if (current_pos->block == next->block) airport_flags ^= next->block;
01774
01775 Station *st = Station::Get(v->targetairport);
01776 if (st->airport_flags & airport_flags) {
01777 v->cur_speed = 0;
01778 v->subspeed = 0;
01779 return false;
01780 }
01781
01782 if (next->block != NOTHING_block) {
01783 SETBITS(st->airport_flags, airport_flags);
01784 }
01785 }
01786 return true;
01787 }
01788
01789 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01790 {
01791 Station *st = Station::Get(v->targetairport);
01792 for (; i < last_terminal; i++) {
01793 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01794
01795 v->state = _airport_terminal_state[i];
01796 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01797 return true;
01798 }
01799 }
01800 return false;
01801 }
01802
01803 static uint GetNumTerminals(const AirportFTAClass *apc)
01804 {
01805 uint num = 0;
01806
01807 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01808
01809 return num;
01810 }
01811
01812 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01813 {
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824 if (apc->terminals[0] > 1) {
01825 const Station *st = Station::Get(v->targetairport);
01826 const AirportFTA *temp = apc->layout[v->pos].next;
01827
01828 while (temp != NULL) {
01829 if (temp->heading == 255) {
01830 if (!(st->airport_flags & temp->block)) {
01831
01832
01833 uint target_group = temp->next_position + 1;
01834
01835
01836
01837
01838 uint group_start = 0;
01839 for (uint i = 1; i < target_group; i++) {
01840 group_start += apc->terminals[i];
01841 }
01842
01843 uint group_end = group_start + apc->terminals[target_group];
01844 if (FreeTerminal(v, group_start, group_end)) return true;
01845 }
01846 } else {
01847
01848
01849 return false;
01850 }
01851 temp = temp->next;
01852 }
01853 }
01854
01855
01856 return FreeTerminal(v, 0, GetNumTerminals(apc));
01857 }
01858
01859 static uint GetNumHelipads(const AirportFTAClass *apc)
01860 {
01861 uint num = 0;
01862
01863 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01864
01865 return num;
01866 }
01867
01868
01869 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01870 {
01871
01872 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01873
01874
01875 if (apc->helipads[0] > 1) {
01876 const Station *st = Station::Get(v->targetairport);
01877 const AirportFTA *temp = apc->layout[v->pos].next;
01878
01879 while (temp != NULL) {
01880 if (temp->heading == 255) {
01881 if (!(st->airport_flags & temp->block)) {
01882
01883
01884
01885 uint target_group = temp->next_position + 1;
01886
01887
01888
01889
01890 uint group_start = 0;
01891 for (uint i = 1; i < target_group; i++) {
01892 group_start += apc->helipads[i];
01893 }
01894
01895 uint group_end = group_start + apc->helipads[target_group];
01896 if (FreeTerminal(v, group_start, group_end)) return true;
01897 }
01898 } else {
01899
01900
01901 return false;
01902 }
01903 temp = temp->next;
01904 }
01905 } else {
01906
01907
01908 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01909 }
01910 return false;
01911 }
01912
01913 static bool AircraftEventHandler(Aircraft *v, int loop)
01914 {
01915 v->tick_counter++;
01916
01917 if (v->vehstatus & VS_CRASHED) {
01918 return HandleCrashedAircraft(v);
01919 }
01920
01921 if (v->vehstatus & VS_STOPPED) return true;
01922
01923
01924 if (v->breakdown_ctr != 0) {
01925 if (v->breakdown_ctr <= 2) {
01926 HandleBrokenAircraft(v);
01927 } else {
01928 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
01929 }
01930 }
01931
01932 HandleAircraftSmoke(v);
01933 ProcessOrders(v);
01934 v->HandleLoading(loop != 0);
01935
01936 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01937
01938 AirportGoToNextPosition(v);
01939
01940 return true;
01941 }
01942
01943 bool Aircraft::Tick()
01944 {
01945 if (!this->IsNormalAircraft()) return true;
01946
01947 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01948
01949 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01950
01951 this->current_order_time++;
01952
01953 for (uint i = 0; i != 2; i++) {
01954
01955 if (!AircraftEventHandler(this, i)) return false;
01956 }
01957
01958 return true;
01959 }
01960
01961
01967 Station *GetTargetAirportIfValid(const Aircraft *v)
01968 {
01969 assert(v->type == VEH_AIRCRAFT);
01970
01971 Station *st = Station::GetIfValid(v->targetairport);
01972 if (st == NULL) return NULL;
01973
01974 return st->airport_tile == INVALID_TILE ? NULL : st;
01975 }
01976
01981 void UpdateAirplanesOnNewStation(const Station *st)
01982 {
01983
01984 const AirportFTAClass *ap = st->Airport();
01985
01986 Aircraft *v;
01987 FOR_ALL_AIRCRAFT(v) {
01988 if (v->IsNormalAircraft()) {
01989 if (v->targetairport == st->index) {
01990
01991
01992 if (v->state >= FLYING) {
01993 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap);
01994 v->state = FLYING;
01995 UpdateAircraftCache(v);
01996
01997
01998 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
01999
02000 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02001 } else {
02002 assert(v->state == ENDTAKEOFF || v->state == HELITAKEOFF);
02003 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02004
02005
02006 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02007 if (ap->layout[cnt].heading == takeofftype) {
02008 v->pos = ap->layout[cnt].position;
02009 UpdateAircraftCache(v);
02010 break;
02011 }
02012 }
02013 }
02014 }
02015 }
02016 }
02017 }