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