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, PaletteID 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->GetAirportSpec()->nof_depots > 0 && CanVehicleUseStation(v, st)) {
00536 v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
00537 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00538 } else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00539 v->current_order.MakeDummy();
00540 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00541 }
00542 }
00543
00544 Money Aircraft::GetRunningCost() const
00545 {
00546 const Engine *e = Engine::Get(this->engine_type);
00547 uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
00548 return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->grffile);
00549 }
00550
00551 void Aircraft::OnNewDay()
00552 {
00553 if (!this->IsNormalAircraft()) return;
00554
00555 if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
00556
00557 CheckOrders(this);
00558
00559 CheckVehicleBreakdown(this);
00560 AgeVehicle(this);
00561 CheckIfAircraftNeedsService(this);
00562
00563 if (this->running_ticks == 0) return;
00564
00565 CommandCost cost(EXPENSES_AIRCRAFT_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00566
00567 this->profit_this_year -= cost.GetCost();
00568 this->running_ticks = 0;
00569
00570 SubtractMoneyFromCompanyFract(this->owner, cost);
00571
00572 SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
00573 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
00574 }
00575
00576 static void HelicopterTickHandler(Aircraft *v)
00577 {
00578 Aircraft *u = v->Next()->Next();
00579
00580 if (u->vehstatus & VS_HIDDEN) return;
00581
00582
00583
00584 if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) {
00585 if (u->cur_speed != 0) {
00586 u->cur_speed++;
00587 if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) {
00588 u->cur_speed = 0;
00589 }
00590 }
00591 } else {
00592 if (u->cur_speed == 0)
00593 u->cur_speed = 0x70;
00594
00595 if (u->cur_speed >= 0x50)
00596 u->cur_speed--;
00597 }
00598
00599 int tick = ++u->tick_counter;
00600 int spd = u->cur_speed >> 4;
00601
00602 SpriteID img;
00603 if (spd == 0) {
00604 u->state = HRS_ROTOR_STOPPED;
00605 img = GetRotorImage(v);
00606 if (u->cur_image == img) return;
00607 } else if (tick >= spd) {
00608 u->tick_counter = 0;
00609 u->state++;
00610 if (u->state > HRS_ROTOR_MOVING_3) u->state = HRS_ROTOR_MOVING_1;
00611 img = GetRotorImage(v);
00612 } else {
00613 return;
00614 }
00615
00616 u->cur_image = img;
00617
00618 VehicleMove(u, true);
00619 }
00620
00621 void SetAircraftPosition(Aircraft *v, int x, int y, int z)
00622 {
00623 v->x_pos = x;
00624 v->y_pos = y;
00625 v->z_pos = z;
00626
00627 v->UpdateViewport(true, false);
00628 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00629
00630 Aircraft *u = v->Next();
00631
00632 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00633 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00634 u->x_pos = x;
00635 u->y_pos = y - ((v->z_pos - GetSlopeZ(safe_x, safe_y)) >> 3);;
00636
00637 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00638 u->z_pos = GetSlopeZ(safe_x, safe_y);
00639 u->cur_image = v->cur_image;
00640
00641 VehicleMove(u, true);
00642
00643 u = u->Next();
00644 if (u != NULL) {
00645 u->x_pos = x;
00646 u->y_pos = y;
00647 u->z_pos = z + 5;
00648
00649 VehicleMove(u, true);
00650 }
00651 }
00652
00656 void HandleAircraftEnterHangar(Aircraft *v)
00657 {
00658 v->subspeed = 0;
00659 v->progress = 0;
00660
00661 Aircraft *u = v->Next();
00662 u->vehstatus |= VS_HIDDEN;
00663 u = u->Next();
00664 if (u != NULL) {
00665 u->vehstatus |= VS_HIDDEN;
00666 u->cur_speed = 0;
00667 }
00668
00669 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00670 }
00671
00672 static void PlayAircraftSound(const Vehicle *v)
00673 {
00674 if (!PlayVehicleSound(v, VSE_START)) {
00675 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00676 }
00677 }
00678
00679
00680 void UpdateAircraftCache(Aircraft *v)
00681 {
00682 uint max_speed = GetVehicleProperty(v, PROP_AIRCRAFT_SPEED, 0);
00683 if (max_speed != 0) {
00684
00685 max_speed = (max_speed * 129) / 10;
00686
00687 v->acache.cached_max_speed = max_speed;
00688 } else {
00689 v->acache.cached_max_speed = 0xFFFF;
00690 }
00691 }
00692
00693
00697 enum AircraftSpeedLimits {
00698 SPEED_LIMIT_TAXI = 50,
00699 SPEED_LIMIT_APPROACH = 230,
00700 SPEED_LIMIT_BROKEN = 320,
00701 SPEED_LIMIT_HOLD = 425,
00702 SPEED_LIMIT_NONE = 0xFFFF
00703 };
00704
00712 static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00713 {
00714 uint spd = v->acceleration * 16;
00715 byte t;
00716
00717
00718
00719 speed_limit *= _settings_game.vehicle.plane_speed;
00720
00721 if (v->acache.cached_max_speed < speed_limit) {
00722 if (v->cur_speed < speed_limit) hard_limit = false;
00723 speed_limit = v->acache.cached_max_speed;
00724 }
00725
00726 speed_limit = min(speed_limit, v->max_speed);
00727
00728 v->subspeed = (t = v->subspeed) + (byte)spd;
00729
00730
00731
00732
00733
00734
00735
00736 if (!hard_limit && v->cur_speed > speed_limit) {
00737 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00738 }
00739
00740 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00741
00742
00743 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00744
00745
00746 if (spd != v->cur_speed) {
00747 v->cur_speed = spd;
00748 if (_settings_client.gui.vehicle_speed)
00749 SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00750 }
00751
00752
00753 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00754
00755 if (!(v->direction & 1)) spd = spd * 3 / 4;
00756
00757 spd += v->progress;
00758 v->progress = (byte)spd;
00759 return spd >> 8;
00760 }
00761
00769 byte GetAircraftFlyingAltitude(const Aircraft *v)
00770 {
00771
00772
00773
00774 byte base_altitude = 150;
00775
00776
00777
00778
00779 switch (v->direction) {
00780 case DIR_N:
00781 case DIR_NE:
00782 case DIR_E:
00783 case DIR_SE:
00784 base_altitude += 10;
00785 break;
00786
00787 default: break;
00788 }
00789
00790
00791 base_altitude += min(20 * (v->max_speed / 200), 90);
00792
00793 return base_altitude;
00794 }
00795
00809 static byte AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *apc)
00810 {
00811 assert(v != NULL);
00812 assert(apc != NULL);
00813
00814
00815
00816
00817 TileIndex tile = 0;
00818
00819 const Station *st = Station::GetIfValid(v->targetairport);
00820 if (st != NULL) {
00821
00822 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00823 }
00824
00825 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00826 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00827
00828 DiagDirection dir;
00829 if (abs(delta_y) < abs(delta_x)) {
00830
00831 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00832 } else {
00833
00834 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00835 }
00836 return apc->entry_points[dir];
00837 }
00838
00839
00840 static void MaybeCrashAirplane(Aircraft *v);
00841
00849 static bool AircraftController(Aircraft *v)
00850 {
00851 int count;
00852
00853
00854 const Station *st = Station::GetIfValid(v->targetairport);
00855
00856 TileIndex tile = INVALID_TILE;
00857 if (st != NULL) {
00858 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00859 }
00860
00861 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00862
00863
00864 if (st == NULL || st->airport_tile == INVALID_TILE) {
00865
00866 if (v->pos >= afc->nofelements) {
00867 v->pos = v->previous_pos = AircraftGetEntryPoint(v, afc);
00868 } else if (v->targetairport != v->current_order.GetDestination()) {
00869
00870 v->state = FLYING;
00871 UpdateAircraftCache(v);
00872 AircraftNextAirportPos_and_Order(v);
00873
00874 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00875 return false;
00876 }
00877 }
00878
00879
00880 const AirportMovingData *amd = afc->MovingData(v->pos);
00881
00882 int x = TileX(tile) * TILE_SIZE;
00883 int y = TileY(tile) * TILE_SIZE;
00884
00885
00886 if (amd->flag & AMED_HELI_RAISE) {
00887 Aircraft *u = v->Next()->Next();
00888
00889
00890 if (u->cur_speed > 32) {
00891 v->cur_speed = 0;
00892 if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00893 } else {
00894 u->cur_speed = 32;
00895 count = UpdateAircraftSpeed(v);
00896 if (count > 0) {
00897 v->tile = 0;
00898
00899
00900 if (v->z_pos >= 184) {
00901 v->cur_speed = 0;
00902 return true;
00903 }
00904 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00905 }
00906 }
00907 return false;
00908 }
00909
00910
00911 if (amd->flag & AMED_HELI_LOWER) {
00912 if (st == NULL) {
00913
00914
00915
00916 v->state = FLYING;
00917 UpdateAircraftCache(v);
00918 AircraftNextAirportPos_and_Order(v);
00919 return false;
00920 }
00921
00922
00923 v->tile = tile;
00924
00925
00926 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
00927
00928 if (z == v->z_pos) {
00929 Vehicle *u = v->Next()->Next();
00930
00931
00932 if (u->cur_speed >= 80) return true;
00933 u->cur_speed += 4;
00934 } else {
00935 count = UpdateAircraftSpeed(v);
00936 if (count > 0) {
00937 if (v->z_pos > z) {
00938 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
00939 } else {
00940 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
00941 }
00942 }
00943 }
00944 return false;
00945 }
00946
00947
00948 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
00949
00950
00951 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
00952
00953
00954 if (dist == 0) {
00955
00956 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
00957
00958
00959 if (dirdiff == DIRDIFF_SAME) {
00960 v->cur_speed = 0;
00961 return true;
00962 }
00963
00964 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
00965
00966 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
00967 v->cur_speed >>= 1;
00968
00969 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00970 return false;
00971 }
00972
00973 if (amd->flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) {
00974 MaybeCrashAirplane(v);
00975 if ((v->vehstatus & VS_CRASHED) != 0) return false;
00976 }
00977
00978 uint speed_limit = SPEED_LIMIT_TAXI;
00979 bool hard_limit = true;
00980
00981 if (amd->flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
00982 if (amd->flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
00983 if (amd->flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
00984 if (amd->flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
00985
00986 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
00987 if (count == 0) return false;
00988
00989 if (v->turn_counter != 0) v->turn_counter--;
00990
00991 do {
00992
00993 GetNewVehiclePosResult gp;
00994
00995 if (dist < 4 || (amd->flag & AMED_LAND)) {
00996
00997 gp.x = (v->x_pos != (x + amd->x)) ?
00998 v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
00999 v->x_pos;
01000 gp.y = (v->y_pos != (y + amd->y)) ?
01001 v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01002 v->y_pos;
01003
01004
01005 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01006
01007 } else {
01008
01009
01010 Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01011 if (newdir != v->direction) {
01012 if (amd->flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) {
01013 if (v->turn_counter == 0 || newdir == v->last_direction) {
01014 if (newdir == v->last_direction) {
01015 v->number_consecutive_turns = 0;
01016 } else {
01017 v->number_consecutive_turns++;
01018 }
01019 v->turn_counter = 2 * _settings_game.vehicle.plane_speed;
01020 v->last_direction = v->direction;
01021 v->direction = newdir;
01022 }
01023
01024
01025 gp = GetNewVehiclePos(v);
01026 } else {
01027 v->cur_speed >>= 1;
01028 v->direction = newdir;
01029
01030
01031
01032
01033
01034
01035 gp.x = v->x_pos;
01036 gp.y = v->y_pos;
01037 gp.new_tile = gp.old_tile = v->tile;
01038 }
01039 } else {
01040 v->number_consecutive_turns = 0;
01041
01042 gp = GetNewVehiclePos(v);
01043 }
01044 }
01045
01046 v->tile = gp.new_tile;
01047
01048 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01049
01050
01051 uint z = v->z_pos;
01052
01053 if (amd->flag & AMED_TAKEOFF) {
01054 z = min(z + 2, GetAircraftFlyingAltitude(v));
01055 }
01056
01057 if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01058
01059 if (amd->flag & AMED_LAND) {
01060 if (st->airport_tile == INVALID_TILE) {
01061
01062 v->state = FLYING;
01063 UpdateAircraftCache(v);
01064 AircraftNextAirportPos_and_Order(v);
01065
01066 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01067 continue;
01068 }
01069
01070 uint curz = GetSlopeZ(x + amd->x, y + amd->y) + 1;
01071
01072
01073 assert(curz <= z);
01074 int t = max(1U, dist - 4);
01075 int delta = z - curz;
01076
01077
01078 if (delta >= t) {
01079 z -= ((z - curz) + t - 1) / t;
01080 }
01081 if (z < curz) z = curz;
01082 }
01083
01084
01085 if (amd->flag & AMED_BRAKE) {
01086 uint curz = GetSlopeZ(x, y) + 1;
01087
01088 if (z > curz) {
01089 z--;
01090 } else if (z < curz) {
01091 z++;
01092 }
01093
01094 }
01095
01096 SetAircraftPosition(v, gp.x, gp.y, z);
01097 } while (--count != 0);
01098 return false;
01099 }
01100
01101
01102 static bool HandleCrashedAircraft(Aircraft *v)
01103 {
01104 v->crashed_counter += 3;
01105
01106 Station *st = GetTargetAirportIfValid(v);
01107
01108
01109 if (v->crashed_counter < 500 && st == NULL && ((v->crashed_counter % 3) == 0) ) {
01110 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01111 v->z_pos -= 1;
01112 if (v->z_pos == z) {
01113 v->crashed_counter = 500;
01114 v->z_pos++;
01115 }
01116 }
01117
01118 if (v->crashed_counter < 650) {
01119 uint32 r;
01120 if (Chance16R(1, 32, r)) {
01121 static const DirDiff delta[] = {
01122 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01123 };
01124
01125 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01126 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01127 r = Random();
01128 CreateEffectVehicleRel(v,
01129 GB(r, 0, 4) - 4,
01130 GB(r, 4, 4) - 4,
01131 GB(r, 8, 4),
01132 EV_EXPLOSION_SMALL);
01133 }
01134 } else if (v->crashed_counter >= 10000) {
01135
01136
01137
01138
01139
01140 if (st != NULL) {
01141 CLRBITS(st->airport_flags, RUNWAY_IN_block);
01142 CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block);
01143 CLRBITS(st->airport_flags, RUNWAY_IN2_block);
01144 }
01145
01146 delete v;
01147
01148 return false;
01149 }
01150
01151 return true;
01152 }
01153
01154 static void HandleBrokenAircraft(Aircraft *v)
01155 {
01156 if (v->breakdown_ctr != 1) {
01157 v->breakdown_ctr = 1;
01158 v->vehstatus |= VS_AIRCRAFT_BROKEN;
01159
01160 if (v->breakdowns_since_last_service != 255)
01161 v->breakdowns_since_last_service++;
01162 SetWindowDirty(WC_VEHICLE_VIEW, v->index);
01163 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01164 }
01165 }
01166
01167
01168 static void HandleAircraftSmoke(Aircraft *v)
01169 {
01170 static const struct {
01171 int8 x;
01172 int8 y;
01173 } smoke_pos[] = {
01174 { 5, 5 },
01175 { 6, 0 },
01176 { 5, -5 },
01177 { 0, -6 },
01178 { -5, -5 },
01179 { -6, 0 },
01180 { -5, 5 },
01181 { 0, 6 }
01182 };
01183
01184 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01185
01186 if (v->cur_speed < 10) {
01187 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01188 v->breakdown_ctr = 0;
01189 return;
01190 }
01191
01192 if ((v->tick_counter & 0x1F) == 0) {
01193 CreateEffectVehicleRel(v,
01194 smoke_pos[v->direction].x,
01195 smoke_pos[v->direction].y,
01196 2,
01197 EV_SMOKE
01198 );
01199 }
01200 }
01201
01202 void HandleMissingAircraftOrders(Aircraft *v)
01203 {
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219 const Station *st = GetTargetAirportIfValid(v);
01220 if (st == NULL) {
01221 CommandCost ret;
01222 CompanyID old_company = _current_company;
01223
01224 _current_company = v->owner;
01225 ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01226 _current_company = old_company;
01227
01228 if (ret.Failed()) CrashAirplane(v);
01229 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01230 v->current_order.Free();
01231 }
01232 }
01233
01234
01235 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01236 {
01237
01238 if (this->state == FLYING) {
01239 AircraftNextAirportPos_and_Order(this);
01240 }
01241
01242
01243 return 0;
01244 }
01245
01246 void Aircraft::MarkDirty()
01247 {
01248 this->UpdateViewport(false, false);
01249 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01250 }
01251
01252
01253 uint Aircraft::Crash(bool flooded)
01254 {
01255 uint pass = Vehicle::Crash(flooded) + 2;
01256 this->crashed_counter = flooded ? 9000 : 0;
01257
01258 return pass;
01259 }
01260
01261 static void CrashAirplane(Aircraft *v)
01262 {
01263 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01264
01265 uint pass = v->Crash();
01266 SetDParam(0, pass);
01267
01268 v->cargo.Truncate(0);
01269 v->Next()->cargo.Truncate(0);
01270 const Station *st = GetTargetAirportIfValid(v);
01271 StringID newsitem;
01272 AIEventVehicleCrashed::CrashReason crash_reason;
01273 if (st == NULL) {
01274 newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL;
01275 crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
01276 } else {
01277 SetDParam(1, st->index);
01278 newsitem = STR_NEWS_AIRCRAFT_CRASH;
01279 crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
01280 }
01281
01282 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
01283
01284 AddVehicleNewsItem(newsitem,
01285 NS_ACCIDENT,
01286 v->index,
01287 st != NULL ? st->index : INVALID_STATION);
01288
01289 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01290 }
01291
01292 static void MaybeCrashAirplane(Aircraft *v)
01293 {
01294 if (_settings_game.vehicle.plane_crashes == 0) return;
01295
01296 Station *st = Station::Get(v->targetairport);
01297
01298
01299 uint32 prob = (0x4000 << _settings_game.vehicle.plane_crashes);
01300 if ((st->Airport()->flags & AirportFTAClass::SHORT_STRIP) &&
01301 (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
01302 !_cheats.no_jetcrash.value) {
01303 prob /= 20;
01304 } else {
01305 prob /= 1500;
01306 }
01307
01308 if (GB(Random(), 0, 22) > prob) return;
01309
01310
01311 for (CargoID i = 0; i < NUM_CARGO; i++) {
01312 st->goods[i].rating = 1;
01313 st->goods[i].cargo.Truncate(0);
01314 }
01315
01316 CrashAirplane(v);
01317 }
01318
01320 static void AircraftEntersTerminal(Aircraft *v)
01321 {
01322 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01323
01324 Station *st = Station::Get(v->targetairport);
01325 v->last_station_visited = v->targetairport;
01326
01327
01328 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01329 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01330 SetDParam(0, st->index);
01331
01332 AddVehicleNewsItem(
01333 STR_NEWS_FIRST_AIRCRAFT_ARRIVAL,
01334 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01335 v->index,
01336 st->index
01337 );
01338 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01339 }
01340
01341 v->BeginLoading();
01342 }
01343
01344 static void AircraftLandAirplane(Aircraft *v)
01345 {
01346 v->UpdateDeltaXY(INVALID_DIR);
01347
01348 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01349 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01350 }
01351 }
01352
01353
01355 void AircraftNextAirportPos_and_Order(Aircraft *v)
01356 {
01357 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01358 v->targetairport = v->current_order.GetDestination();
01359 }
01360
01361 const Station *st = GetTargetAirportIfValid(v);
01362 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01363 v->pos = v->previous_pos = AircraftGetEntryPoint(v, apc);
01364 }
01365
01366 void AircraftLeaveHangar(Aircraft *v)
01367 {
01368 v->cur_speed = 0;
01369 v->subspeed = 0;
01370 v->progress = 0;
01371 v->direction = DIR_SE;
01372 v->vehstatus &= ~VS_HIDDEN;
01373 {
01374 Vehicle *u = v->Next();
01375 u->vehstatus &= ~VS_HIDDEN;
01376
01377
01378 u = u->Next();
01379 if (u != NULL) {
01380 u->vehstatus &= ~VS_HIDDEN;
01381 u->cur_speed = 80;
01382 }
01383 }
01384
01385 VehicleServiceInDepot(v);
01386 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01387 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01388 SetWindowClassesDirty(WC_AIRCRAFT_LIST);
01389 }
01390
01394 static void AircraftEventHandler_EnterTerminal(Aircraft *v, const AirportFTAClass *apc)
01395 {
01396 AircraftEntersTerminal(v);
01397 v->state = apc->layout[v->pos].heading;
01398 }
01399
01400 static void AircraftEventHandler_EnterHangar(Aircraft *v, const AirportFTAClass *apc)
01401 {
01402 VehicleEnterDepot(v);
01403 v->state = apc->layout[v->pos].heading;
01404 }
01405
01407 static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *apc)
01408 {
01409
01410 if (v->previous_pos != v->pos) {
01411 AircraftEventHandler_EnterHangar(v, apc);
01412 return;
01413 }
01414
01415
01416 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01417 v->current_order.Free();
01418 return;
01419 }
01420
01421 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01422 !v->current_order.IsType(OT_GOTO_DEPOT))
01423 return;
01424
01425
01426 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01427
01428
01429 if (v->current_order.GetDestination() == v->targetairport) {
01430
01431
01432 if (v->subtype == AIR_HELICOPTER) {
01433 if (!AirportFindFreeHelipad(v, apc)) return;
01434 } else {
01435 if (!AirportFindFreeTerminal(v, apc)) return;
01436 }
01437 } else {
01438
01439 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01440 }
01441 AircraftLeaveHangar(v);
01442 AirportMove(v, apc);
01443 }
01444
01446 static void AircraftEventHandler_AtTerminal(Aircraft *v, const AirportFTAClass *apc)
01447 {
01448
01449 if (v->previous_pos != v->pos) {
01450 AircraftEventHandler_EnterTerminal(v, apc);
01451
01452
01453 if (_settings_game.order.serviceathelipad) {
01454 if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01455
01456 v->date_of_last_service = _date;
01457 v->breakdowns_since_last_service = 0;
01458 v->reliability = Engine::Get(v->engine_type)->reliability;
01459 SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
01460 }
01461 }
01462 return;
01463 }
01464
01465 if (v->current_order.IsType(OT_NOTHING)) return;
01466
01467
01468 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01469
01470
01471
01472
01473 bool go_to_hangar = false;
01474 switch (v->current_order.GetType()) {
01475 case OT_GOTO_STATION:
01476 break;
01477 case OT_GOTO_DEPOT:
01478 go_to_hangar = v->current_order.GetDestination() == v->targetairport;
01479 break;
01480 case OT_CONDITIONAL:
01481
01482
01483
01484 return;
01485 default:
01486 v->current_order.Free();
01487 go_to_hangar = Station::Get(v->targetairport)->GetAirportSpec()->nof_depots != 0;
01488 }
01489
01490 if (go_to_hangar) {
01491 v->state = HANGAR;
01492 } else {
01493
01494 v->state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01495 }
01496 AirportMove(v, apc);
01497 }
01498
01499 static void AircraftEventHandler_General(Aircraft *v, const AirportFTAClass *apc)
01500 {
01501 error("OK, you shouldn't be here, check your Airport Scheme!");
01502 }
01503
01504 static void AircraftEventHandler_TakeOff(Aircraft *v, const AirportFTAClass *apc)
01505 {
01506 PlayAircraftSound(v);
01507 v->state = STARTTAKEOFF;
01508 }
01509
01510 static void AircraftEventHandler_StartTakeOff(Aircraft *v, const AirportFTAClass *apc)
01511 {
01512 v->state = ENDTAKEOFF;
01513 v->UpdateDeltaXY(INVALID_DIR);
01514 }
01515
01516 static void AircraftEventHandler_EndTakeOff(Aircraft *v, const AirportFTAClass *apc)
01517 {
01518 v->state = FLYING;
01519
01520 AircraftNextAirportPos_and_Order(v);
01521 }
01522
01523 static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass *apc)
01524 {
01525 v->state = FLYING;
01526 v->UpdateDeltaXY(INVALID_DIR);
01527
01528
01529 AircraftNextAirportPos_and_Order(v);
01530
01531
01532 if (v->NeedsAutomaticServicing()) {
01533 _current_company = v->owner;
01534 DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01535 _current_company = OWNER_NONE;
01536 }
01537 }
01538
01539 static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
01540 {
01541 Station *st = Station::Get(v->targetairport);
01542
01543
01544 if ((apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES)) &&
01545 st->airport_tile != INVALID_TILE &&
01546 (st->owner == OWNER_NONE || st->owner == v->owner)) {
01547
01548
01549
01550 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01551 const AirportFTA *current = apc->layout[v->pos].next;
01552 while (current != NULL) {
01553 if (current->heading == landingtype) {
01554
01555
01556
01557 uint16 tcur_speed = v->cur_speed;
01558 uint16 tsubspeed = v->subspeed;
01559 if (!AirportHasBlock(v, current, apc)) {
01560 v->state = landingtype;
01561
01562
01563
01564 v->pos = current->next_position;
01565 SETBITS(st->airport_flags, apc->layout[v->pos].block);
01566 return;
01567 }
01568 v->cur_speed = tcur_speed;
01569 v->subspeed = tsubspeed;
01570 }
01571 current = current->next;
01572 }
01573 }
01574 v->state = FLYING;
01575 v->pos = apc->layout[v->pos].next_position;
01576 }
01577
01578 static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *apc)
01579 {
01580 v->state = ENDLANDING;
01581 AircraftLandAirplane(v);
01582
01583
01584 if (v->NeedsAutomaticServicing()) {
01585 _current_company = v->owner;
01586 DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01587 _current_company = OWNER_NONE;
01588 }
01589 }
01590
01591 static void AircraftEventHandler_HeliLanding(Aircraft *v, const AirportFTAClass *apc)
01592 {
01593 v->state = HELIENDLANDING;
01594 v->UpdateDeltaXY(INVALID_DIR);
01595 }
01596
01597 static void AircraftEventHandler_EndLanding(Aircraft *v, const AirportFTAClass *apc)
01598 {
01599
01600 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01601
01602
01603
01604
01605
01606 if (v->current_order.IsType(OT_GOTO_STATION)) {
01607 if (AirportFindFreeTerminal(v, apc)) return;
01608 }
01609 v->state = HANGAR;
01610
01611 }
01612
01613 static void AircraftEventHandler_HeliEndLanding(Aircraft *v, const AirportFTAClass *apc)
01614 {
01615
01616 if (AirportHasBlock(v, &apc->layout[v->pos], apc)) return;
01617
01618
01619
01620
01621
01622
01623
01624
01625 if (v->current_order.IsType(OT_GOTO_STATION)) {
01626 if (AirportFindFreeHelipad(v, apc)) return;
01627 }
01628 const AirportSpec *as = Station::Get(v->targetairport)->GetAirportSpec();
01629 v->state = (as->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01630 }
01631
01632 typedef void AircraftStateHandler(Aircraft *v, const AirportFTAClass *apc);
01633 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01634 AircraftEventHandler_General,
01635 AircraftEventHandler_InHangar,
01636 AircraftEventHandler_AtTerminal,
01637 AircraftEventHandler_AtTerminal,
01638 AircraftEventHandler_AtTerminal,
01639 AircraftEventHandler_AtTerminal,
01640 AircraftEventHandler_AtTerminal,
01641 AircraftEventHandler_AtTerminal,
01642 AircraftEventHandler_AtTerminal,
01643 AircraftEventHandler_AtTerminal,
01644 AircraftEventHandler_TakeOff,
01645 AircraftEventHandler_StartTakeOff,
01646 AircraftEventHandler_EndTakeOff,
01647 AircraftEventHandler_HeliTakeOff,
01648 AircraftEventHandler_Flying,
01649 AircraftEventHandler_Landing,
01650 AircraftEventHandler_EndLanding,
01651 AircraftEventHandler_HeliLanding,
01652 AircraftEventHandler_HeliEndLanding,
01653 AircraftEventHandler_AtTerminal,
01654 AircraftEventHandler_AtTerminal,
01655 AircraftEventHandler_AtTerminal,
01656 AircraftEventHandler_AtTerminal,
01657 };
01658
01659 static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
01660 {
01661
01662 if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) {
01663 Station *st = Station::Get(v->targetairport);
01664
01665 CLRBITS(st->airport_flags, apc->layout[v->previous_pos].block);
01666 }
01667 }
01668
01669 static void AirportGoToNextPosition(Aircraft *v)
01670 {
01671
01672 if (!AircraftController(v)) return;
01673
01674 const AirportFTAClass *apc = Station::Get(v->targetairport)->Airport();
01675
01676 AirportClearBlock(v, apc);
01677 AirportMove(v, apc);
01678 }
01679
01680
01681 static bool AirportMove(Aircraft *v, const AirportFTAClass *apc)
01682 {
01683
01684 if (v->pos >= apc->nofelements) {
01685 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->pos, apc->nofelements-1);
01686 assert(v->pos < apc->nofelements);
01687 }
01688
01689 const AirportFTA *current = &apc->layout[v->pos];
01690
01691 if (current->heading == v->state) {
01692 byte prev_pos = v->pos;
01693 byte prev_state = v->state;
01694 _aircraft_state_handlers[v->state](v, apc);
01695 if (v->state != FLYING) v->previous_pos = prev_pos;
01696 if (v->state != prev_state || v->pos != prev_pos) UpdateAircraftCache(v);
01697 return true;
01698 }
01699
01700 v->previous_pos = v->pos;
01701
01702
01703 if (current->next == NULL) {
01704 if (AirportSetBlocks(v, current, apc)) {
01705 v->pos = current->next_position;
01706 UpdateAircraftCache(v);
01707 }
01708 return false;
01709 }
01710
01711
01712
01713 do {
01714 if (v->state == current->heading || current->heading == TO_ALL) {
01715 if (AirportSetBlocks(v, current, apc)) {
01716 v->pos = current->next_position;
01717 UpdateAircraftCache(v);
01718 }
01719 return false;
01720 }
01721 current = current->next;
01722 } while (current != NULL);
01723
01724 DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d) for vehicle %d", v->pos, v->state, v->index);
01725 NOT_REACHED();
01726 }
01727
01728
01729 static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01730 {
01731 const AirportFTA *reference = &apc->layout[v->pos];
01732 const AirportFTA *next = &apc->layout[current_pos->next_position];
01733
01734
01735 if (apc->layout[current_pos->position].block != next->block) {
01736 const Station *st = Station::Get(v->targetairport);
01737 uint64 airport_flags = next->block;
01738
01739
01740 if (current_pos != reference && current_pos->block != NOTHING_block) {
01741 airport_flags |= current_pos->block;
01742 }
01743
01744 if (st->airport_flags & airport_flags) {
01745 v->cur_speed = 0;
01746 v->subspeed = 0;
01747 return true;
01748 }
01749 }
01750 return false;
01751 }
01752
01760 static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01761 {
01762 const AirportFTA *next = &apc->layout[current_pos->next_position];
01763 const AirportFTA *reference = &apc->layout[v->pos];
01764
01765
01766 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01767 uint64 airport_flags = next->block;
01768
01769
01770 const AirportFTA *current = current_pos;
01771 if (current == reference) current = current->next;
01772 while (current != NULL) {
01773 if (current->heading == current_pos->heading && current->block != 0) {
01774 airport_flags |= current->block;
01775 break;
01776 }
01777 current = current->next;
01778 };
01779
01780
01781
01782 if (current_pos->block == next->block) airport_flags ^= next->block;
01783
01784 Station *st = Station::Get(v->targetairport);
01785 if (st->airport_flags & airport_flags) {
01786 v->cur_speed = 0;
01787 v->subspeed = 0;
01788 return false;
01789 }
01790
01791 if (next->block != NOTHING_block) {
01792 SETBITS(st->airport_flags, airport_flags);
01793 }
01794 }
01795 return true;
01796 }
01797
01798 static bool FreeTerminal(Aircraft *v, byte i, byte last_terminal)
01799 {
01800 Station *st = Station::Get(v->targetairport);
01801 for (; i < last_terminal; i++) {
01802 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01803
01804 v->state = _airport_terminal_state[i];
01805 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01806 return true;
01807 }
01808 }
01809 return false;
01810 }
01811
01812 static uint GetNumTerminals(const AirportFTAClass *apc)
01813 {
01814 uint num = 0;
01815
01816 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01817
01818 return num;
01819 }
01820
01821 static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc)
01822 {
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833 if (apc->terminals[0] > 1) {
01834 const Station *st = Station::Get(v->targetairport);
01835 const AirportFTA *temp = apc->layout[v->pos].next;
01836
01837 while (temp != NULL) {
01838 if (temp->heading == 255) {
01839 if (!(st->airport_flags & temp->block)) {
01840
01841
01842 uint target_group = temp->next_position + 1;
01843
01844
01845
01846
01847 uint group_start = 0;
01848 for (uint i = 1; i < target_group; i++) {
01849 group_start += apc->terminals[i];
01850 }
01851
01852 uint group_end = group_start + apc->terminals[target_group];
01853 if (FreeTerminal(v, group_start, group_end)) return true;
01854 }
01855 } else {
01856
01857
01858 return false;
01859 }
01860 temp = temp->next;
01861 }
01862 }
01863
01864
01865 return FreeTerminal(v, 0, GetNumTerminals(apc));
01866 }
01867
01868 static uint GetNumHelipads(const AirportFTAClass *apc)
01869 {
01870 uint num = 0;
01871
01872 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01873
01874 return num;
01875 }
01876
01877
01878 static bool AirportFindFreeHelipad(Aircraft *v, const AirportFTAClass *apc)
01879 {
01880
01881 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01882
01883
01884 if (apc->helipads[0] > 1) {
01885 const Station *st = Station::Get(v->targetairport);
01886 const AirportFTA *temp = apc->layout[v->pos].next;
01887
01888 while (temp != NULL) {
01889 if (temp->heading == 255) {
01890 if (!(st->airport_flags & temp->block)) {
01891
01892
01893
01894 uint target_group = temp->next_position + 1;
01895
01896
01897
01898
01899 uint group_start = 0;
01900 for (uint i = 1; i < target_group; i++) {
01901 group_start += apc->helipads[i];
01902 }
01903
01904 uint group_end = group_start + apc->helipads[target_group];
01905 if (FreeTerminal(v, group_start, group_end)) return true;
01906 }
01907 } else {
01908
01909
01910 return false;
01911 }
01912 temp = temp->next;
01913 }
01914 } else {
01915
01916
01917 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01918 }
01919 return false;
01920 }
01921
01922 static bool AircraftEventHandler(Aircraft *v, int loop)
01923 {
01924 v->tick_counter++;
01925
01926 if (v->vehstatus & VS_CRASHED) {
01927 return HandleCrashedAircraft(v);
01928 }
01929
01930 if (v->vehstatus & VS_STOPPED) return true;
01931
01932
01933 if (v->breakdown_ctr != 0) {
01934 if (v->breakdown_ctr <= 2) {
01935 HandleBrokenAircraft(v);
01936 } else {
01937 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
01938 }
01939 }
01940
01941 HandleAircraftSmoke(v);
01942 ProcessOrders(v);
01943 v->HandleLoading(loop != 0);
01944
01945 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return true;
01946
01947 AirportGoToNextPosition(v);
01948
01949 return true;
01950 }
01951
01952 bool Aircraft::Tick()
01953 {
01954 if (!this->IsNormalAircraft()) return true;
01955
01956 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
01957
01958 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
01959
01960 this->current_order_time++;
01961
01962 for (uint i = 0; i != 2; i++) {
01963
01964 if (!AircraftEventHandler(this, i)) return false;
01965 }
01966
01967 return true;
01968 }
01969
01970
01976 Station *GetTargetAirportIfValid(const Aircraft *v)
01977 {
01978 assert(v->type == VEH_AIRCRAFT);
01979
01980 Station *st = Station::GetIfValid(v->targetairport);
01981 if (st == NULL) return NULL;
01982
01983 return st->airport_tile == INVALID_TILE ? NULL : st;
01984 }
01985
01990 void UpdateAirplanesOnNewStation(const Station *st)
01991 {
01992
01993 const AirportFTAClass *ap = st->Airport();
01994
01995 Aircraft *v;
01996 FOR_ALL_AIRCRAFT(v) {
01997 if (v->IsNormalAircraft()) {
01998 if (v->targetairport == st->index) {
01999
02000
02001 if (v->state >= FLYING) {
02002 v->pos = v->previous_pos = AircraftGetEntryPoint(v, ap);
02003 v->state = FLYING;
02004 UpdateAircraftCache(v);
02005
02006
02007 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
02008
02009 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02010 } else {
02011 assert(v->state == ENDTAKEOFF || v->state == HELITAKEOFF);
02012 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02013
02014
02015 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02016 if (ap->layout[cnt].heading == takeofftype) {
02017 v->pos = ap->layout[cnt].position;
02018 UpdateAircraftCache(v);
02019 break;
02020 }
02021 }
02022 }
02023 }
02024 }
02025 }
02026 }