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