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