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 VehicleMove(v, false);
00401 VehicleMove(u, false);
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 VehicleMove(w, false);
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 VehicleMove(u, true);
00696 }
00697
00698 void SetAircraftPosition(Vehicle *v, int x, int y, int z)
00699 {
00700 v->x_pos = x;
00701 v->y_pos = y;
00702 v->z_pos = z;
00703
00704 v->cur_image = v->GetImage(v->direction);
00705 if (v->subtype == AIR_HELICOPTER) v->Next()->Next()->cur_image = GetRotorImage(v);
00706
00707 VehicleMove(v, true);
00708
00709 Vehicle *u = v->Next();
00710
00711 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00712 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00713 u->x_pos = x;
00714 u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);;
00715
00716 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00717 u->z_pos = GetSlopeZ(safe_x, safe_y);
00718 u->cur_image = v->cur_image;
00719
00720 VehicleMove(u, true);
00721
00722 u = u->Next();
00723 if (u != NULL) {
00724 u->x_pos = x;
00725 u->y_pos = y;
00726 u->z_pos = z + 5;
00727
00728 VehicleMove(u, true);
00729 }
00730 }
00731
00735 void HandleAircraftEnterHangar(Vehicle *v)
00736 {
00737 v->subspeed = 0;
00738 v->progress = 0;
00739
00740 Vehicle *u = v->Next();
00741 u->vehstatus |= VS_HIDDEN;
00742 u = u->Next();
00743 if (u != NULL) {
00744 u->vehstatus |= VS_HIDDEN;
00745 u->cur_speed = 0;
00746 }
00747
00748 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
00749 }
00750
00751 static void PlayAircraftSound(const Vehicle *v)
00752 {
00753 if (!PlayVehicleSound(v, VSE_START)) {
00754 SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
00755 }
00756 }
00757
00758
00759 void UpdateAircraftCache(Vehicle *v)
00760 {
00761 uint max_speed = GetVehicleProperty(v, 0x0C, 0);
00762 if (max_speed != 0) {
00763
00764 max_speed = (max_speed * 129) / 10;
00765
00766 v->u.air.cached_max_speed = max_speed;
00767 } else {
00768 v->u.air.cached_max_speed = 0xFFFF;
00769 }
00770 }
00771
00772
00776 enum AircraftSpeedLimits {
00777 SPEED_LIMIT_TAXI = 50,
00778 SPEED_LIMIT_APPROACH = 230,
00779 SPEED_LIMIT_BROKEN = 320,
00780 SPEED_LIMIT_HOLD = 425,
00781 SPEED_LIMIT_NONE = 0xFFFF
00782 };
00783
00791 static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
00792 {
00793 uint spd = v->acceleration * 16;
00794 byte t;
00795
00796
00797
00798 speed_limit *= _settings_game.vehicle.plane_speed;
00799
00800 if (v->u.air.cached_max_speed < speed_limit) {
00801 if (v->cur_speed < speed_limit) hard_limit = false;
00802 speed_limit = v->u.air.cached_max_speed;
00803 }
00804
00805 speed_limit = min(speed_limit, v->max_speed);
00806
00807 v->subspeed = (t=v->subspeed) + (byte)spd;
00808
00809
00810
00811
00812
00813
00814
00815 if (!hard_limit && v->cur_speed > speed_limit) {
00816 speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
00817 }
00818
00819 spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
00820
00821
00822 if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
00823
00824
00825 if (spd != v->cur_speed) {
00826 v->cur_speed = spd;
00827 if (_settings_client.gui.vehicle_speed)
00828 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00829 }
00830
00831
00832 if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
00833
00834 if (!(v->direction & 1)) spd = spd * 3 / 4;
00835
00836 spd += v->progress;
00837 v->progress = (byte)spd;
00838 return spd >> 8;
00839 }
00840
00848 byte GetAircraftFlyingAltitude(const Vehicle *v)
00849 {
00850
00851
00852
00853 byte base_altitude = 150;
00854
00855
00856
00857
00858 switch (v->direction) {
00859 case DIR_N:
00860 case DIR_NE:
00861 case DIR_E:
00862 case DIR_SE:
00863 base_altitude += 10;
00864 break;
00865
00866 default: break;
00867 }
00868
00869
00870 base_altitude += min(20 * (v->max_speed / 200), 90);
00871
00872 return base_altitude;
00873 }
00874
00888 static byte AircraftGetEntryPoint(const Vehicle *v, const AirportFTAClass *apc)
00889 {
00890 assert(v != NULL);
00891 assert(apc != NULL);
00892
00893
00894
00895
00896 TileIndex tile = 0;
00897
00898 if (IsValidStationID(v->u.air.targetairport)) {
00899 const Station *st = GetStation(v->u.air.targetairport);
00900
00901 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00902 }
00903
00904 int delta_x = v->x_pos - TileX(tile) * TILE_SIZE;
00905 int delta_y = v->y_pos - TileY(tile) * TILE_SIZE;
00906
00907 DiagDirection dir;
00908 if (abs(delta_y) < abs(delta_x)) {
00909
00910 dir = delta_x < 0 ? DIAGDIR_NE : DIAGDIR_SW;
00911 } else {
00912
00913 dir = delta_y < 0 ? DIAGDIR_NW : DIAGDIR_SE;
00914 }
00915 return apc->entry_points[dir];
00916 }
00917
00925 static bool AircraftController(Vehicle *v)
00926 {
00927 int count;
00928
00929
00930 const Station *st = IsValidStationID(v->u.air.targetairport) ? GetStation(v->u.air.targetairport) : NULL;
00931
00932 TileIndex tile = INVALID_TILE;
00933 if (st != NULL) {
00934 tile = (st->airport_tile != INVALID_TILE) ? st->airport_tile : st->xy;
00935 }
00936
00937 const AirportFTAClass *afc = tile == INVALID_TILE ? GetAirport(AT_DUMMY) : st->Airport();
00938
00939
00940 if (st == NULL || st->airport_tile == INVALID_TILE) {
00941
00942 if (v->u.air.pos >= afc->nofelements) {
00943 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, afc);
00944 } else if (v->u.air.targetairport != v->current_order.GetDestination()) {
00945
00946 v->u.air.state = FLYING;
00947 UpdateAircraftCache(v);
00948 AircraftNextAirportPos_and_Order(v);
00949
00950 SetAircraftPosition(v, v->x_pos, v->y_pos, GetAircraftFlyingAltitude(v));
00951 return false;
00952 }
00953 }
00954
00955
00956 const AirportMovingData *amd = afc->MovingData(v->u.air.pos);
00957
00958 int x = TileX(tile) * TILE_SIZE;
00959 int y = TileY(tile) * TILE_SIZE;
00960
00961
00962 if (amd->flag & AMED_HELI_RAISE) {
00963 Vehicle *u = v->Next()->Next();
00964
00965
00966 if (u->cur_speed > 32) {
00967 v->cur_speed = 0;
00968 if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v);
00969 } else {
00970 u->cur_speed = 32;
00971 count = UpdateAircraftSpeed(v);
00972 if (count > 0) {
00973 v->tile = 0;
00974
00975
00976 if (v->z_pos >= 184) {
00977 v->cur_speed = 0;
00978 return true;
00979 }
00980 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, 184));
00981 }
00982 }
00983 return false;
00984 }
00985
00986
00987 if (amd->flag & AMED_HELI_LOWER) {
00988 if (st == NULL) {
00989
00990
00991
00992 v->u.air.state = FLYING;
00993 UpdateAircraftCache(v);
00994 AircraftNextAirportPos_and_Order(v);
00995 return false;
00996 }
00997
00998
00999 v->tile = tile;
01000
01001
01002 int z = GetSlopeZ(x, y) + 1 + afc->delta_z;
01003
01004 if (z == v->z_pos) {
01005 Vehicle *u = v->Next()->Next();
01006
01007
01008 if (u->cur_speed >= 80) return true;
01009 u->cur_speed += 4;
01010 } else {
01011 count = UpdateAircraftSpeed(v);
01012 if (count > 0) {
01013 if (v->z_pos > z) {
01014 SetAircraftPosition(v, v->x_pos, v->y_pos, max(v->z_pos - count, z));
01015 } else {
01016 SetAircraftPosition(v, v->x_pos, v->y_pos, min(v->z_pos + count, z));
01017 }
01018 }
01019 }
01020 return false;
01021 }
01022
01023
01024 uint dist = abs(x + amd->x - v->x_pos) + abs(y + amd->y - v->y_pos);
01025
01026
01027 if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) return true;
01028
01029
01030 if (dist == 0) {
01031
01032 DirDiff dirdiff = DirDifference(amd->direction, v->direction);
01033
01034
01035 if (dirdiff == DIRDIFF_SAME) {
01036 v->cur_speed = 0;
01037 return true;
01038 }
01039
01040 if (!UpdateAircraftSpeed(v, SPEED_LIMIT_TAXI)) return false;
01041
01042 v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT);
01043 v->cur_speed >>= 1;
01044
01045 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01046 return false;
01047 }
01048
01049 uint speed_limit = SPEED_LIMIT_TAXI;
01050 bool hard_limit = true;
01051
01052 if (amd->flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE;
01053 if (amd->flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; }
01054 if (amd->flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; }
01055 if (amd->flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; }
01056
01057 count = UpdateAircraftSpeed(v, speed_limit, hard_limit);
01058 if (count == 0) return false;
01059
01060 if (v->load_unload_time_rem != 0) v->load_unload_time_rem--;
01061
01062 do {
01063
01064 GetNewVehiclePosResult gp;
01065
01066 if (dist < 4 || amd->flag & AMED_LAND) {
01067
01068 gp.x = (v->x_pos != (x + amd->x)) ?
01069 v->x_pos + ((x + amd->x > v->x_pos) ? 1 : -1) :
01070 v->x_pos;
01071 gp.y = (v->y_pos != (y + amd->y)) ?
01072 v->y_pos + ((y + amd->y > v->y_pos) ? 1 : -1) :
01073 v->y_pos;
01074
01075
01076 gp.new_tile = (st->airport_type == AT_OILRIG) ? st->airport_tile : TileVirtXY(gp.x, gp.y);
01077
01078 } else {
01079
01080
01081 Direction newdir = GetDirectionTowards(v, x + amd->x, y + amd->y);
01082 if (newdir != v->direction) {
01083 v->direction = newdir;
01084 if (amd->flag & AMED_SLOWTURN) {
01085 if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8;
01086 } else {
01087 v->cur_speed >>= 1;
01088 }
01089 }
01090
01091
01092 gp = GetNewVehiclePos(v);
01093 }
01094
01095 v->tile = gp.new_tile;
01096
01097 if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0;
01098
01099
01100 uint z = v->z_pos;
01101
01102 if (amd->flag & AMED_TAKEOFF) {
01103 z = min(z + 2, GetAircraftFlyingAltitude(v));
01104 }
01105
01106 if ((amd->flag & AMED_HOLD) && (z > 150)) z--;
01107
01108 if (amd->flag & AMED_LAND) {
01109 if (st->airport_tile == INVALID_TILE) {
01110
01111 v->u.air.state = FLYING;
01112 UpdateAircraftCache(v);
01113 AircraftNextAirportPos_and_Order(v);
01114
01115 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
01116 continue;
01117 }
01118
01119 uint curz = GetSlopeZ(x, y) + 1;
01120
01121 if (curz > z) {
01122 z++;
01123 } else {
01124 int t = max(1U, dist - 4);
01125
01126 z -= ((z - curz) + t - 1) / t;
01127 if (z < curz) z = curz;
01128 }
01129 }
01130
01131
01132 if (amd->flag & AMED_BRAKE) {
01133 uint curz = GetSlopeZ(x, y) + 1;
01134
01135 if (z > curz) {
01136 z--;
01137 } else if (z < curz) {
01138 z++;
01139 }
01140
01141 }
01142
01143 SetAircraftPosition(v, gp.x, gp.y, z);
01144 } while (--count != 0);
01145 return false;
01146 }
01147
01148
01149 static void HandleCrashedAircraft(Vehicle *v)
01150 {
01151 v->u.air.crashed_counter += 3;
01152
01153 Station *st = GetTargetAirportIfValid(v);
01154
01155
01156 if (v->u.air.crashed_counter < 500 && st == NULL && ((v->u.air.crashed_counter % 3) == 0) ) {
01157 uint z = GetSlopeZ(v->x_pos, v->y_pos);
01158 v->z_pos -= 1;
01159 if (v->z_pos == z) {
01160 v->u.air.crashed_counter = 500;
01161 v->z_pos++;
01162 }
01163 }
01164
01165 if (v->u.air.crashed_counter < 650) {
01166 uint32 r;
01167 if (Chance16R(1,32,r)) {
01168 static const DirDiff delta[] = {
01169 DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
01170 };
01171
01172 v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]);
01173 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01174 r = Random();
01175 CreateEffectVehicleRel(v,
01176 GB(r, 0, 4) - 4,
01177 GB(r, 4, 4) - 4,
01178 GB(r, 8, 4),
01179 EV_EXPLOSION_SMALL);
01180 }
01181 } else if (v->u.air.crashed_counter >= 10000) {
01182
01183
01184
01185
01186
01187 if (st != NULL) {
01188 CLRBITS(st->airport_flags, RUNWAY_IN_block);
01189 CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block);
01190 CLRBITS(st->airport_flags, RUNWAY_IN2_block);
01191 }
01192
01193 delete v;
01194 }
01195 }
01196
01197 static void HandleBrokenAircraft(Vehicle *v)
01198 {
01199 if (v->breakdown_ctr != 1) {
01200 v->breakdown_ctr = 1;
01201 v->vehstatus |= VS_AIRCRAFT_BROKEN;
01202
01203 if (v->breakdowns_since_last_service != 255)
01204 v->breakdowns_since_last_service++;
01205 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01206 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01207 }
01208 }
01209
01210
01211 static void HandleAircraftSmoke(Vehicle *v)
01212 {
01213 static const struct {
01214 int8 x;
01215 int8 y;
01216 } smoke_pos[] = {
01217 { 5, 5 },
01218 { 6, 0 },
01219 { 5, -5 },
01220 { 0, -6 },
01221 { -5, -5 },
01222 { -6, 0 },
01223 { -5, 5 },
01224 { 0, 6 }
01225 };
01226
01227 if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
01228
01229 if (v->cur_speed < 10) {
01230 v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
01231 v->breakdown_ctr = 0;
01232 return;
01233 }
01234
01235 if ((v->tick_counter & 0x1F) == 0) {
01236 CreateEffectVehicleRel(v,
01237 smoke_pos[v->direction].x,
01238 smoke_pos[v->direction].y,
01239 2,
01240 EV_SMOKE
01241 );
01242 }
01243 }
01244
01245 void HandleMissingAircraftOrders(Vehicle *v)
01246 {
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262 const Station *st = GetTargetAirportIfValid(v);
01263 if (st == NULL) {
01264 CommandCost ret;
01265 CompanyID old_company = _current_company;
01266
01267 _current_company = v->owner;
01268 ret = DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01269 _current_company = old_company;
01270
01271 if (CmdFailed(ret)) CrashAirplane(v);
01272 } else if (!v->current_order.IsType(OT_GOTO_DEPOT)) {
01273 v->current_order.Free();
01274 }
01275 }
01276
01277
01278 TileIndex Aircraft::GetOrderStationLocation(StationID station)
01279 {
01280
01281 if (this->u.air.state == FLYING) {
01282 AircraftNextAirportPos_and_Order(this);
01283 }
01284
01285
01286 return 0;
01287 }
01288
01289 void Aircraft::MarkDirty()
01290 {
01291 this->cur_image = this->GetImage(this->direction);
01292 if (this->subtype == AIR_HELICOPTER) this->Next()->Next()->cur_image = GetRotorImage(this);
01293 MarkSingleVehicleDirty(this);
01294 }
01295
01296 static void CrashAirplane(Vehicle *v)
01297 {
01298 v->vehstatus |= VS_CRASHED;
01299 v->u.air.crashed_counter = 0;
01300
01301 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
01302
01303 InvalidateWindow(WC_VEHICLE_VIEW, v->index);
01304
01305 uint amt = 2;
01306 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count();
01307 SetDParam(0, amt);
01308
01309 v->cargo.Truncate(0);
01310 v->Next()->cargo.Truncate(0);
01311 const Station *st = GetTargetAirportIfValid(v);
01312 StringID newsitem;
01313 AIEventVehicleCrashed::CrashReason crash_reason;
01314 if (st == NULL) {
01315 newsitem = STR_PLANE_CRASH_OUT_OF_FUEL;
01316 crash_reason = AIEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT;
01317 } else {
01318 SetDParam(1, st->index);
01319 newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL;
01320 crash_reason = AIEventVehicleCrashed::CRASH_PLANE_LANDING;
01321 }
01322
01323 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, crash_reason));
01324
01325 AddNewsItem(newsitem,
01326 NS_ACCIDENT_VEHICLE,
01327 v->index,
01328 0);
01329
01330 SndPlayVehicleFx(SND_12_EXPLOSION, v);
01331 }
01332
01333 static void MaybeCrashAirplane(Vehicle *v)
01334 {
01335 Station *st = GetStation(v->u.air.targetairport);
01336
01337
01338 uint16 prob = 0x10000 / 1500;
01339 if (st->Airport()->flags & AirportFTAClass::SHORT_STRIP &&
01340 AircraftVehInfo(v->engine_type)->subtype & AIR_FAST &&
01341 !_cheats.no_jetcrash.value) {
01342 prob = 0x10000 / 20;
01343 }
01344
01345 if (GB(Random(), 0, 16) > prob) return;
01346
01347
01348 for (CargoID i = 0; i < NUM_CARGO; i++) {
01349 st->goods[i].rating = 1;
01350 st->goods[i].cargo.Truncate(0);
01351 }
01352
01353 CrashAirplane(v);
01354 }
01355
01357 static void AircraftEntersTerminal(Vehicle *v)
01358 {
01359 if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
01360
01361 Station *st = GetStation(v->u.air.targetairport);
01362 v->last_station_visited = v->u.air.targetairport;
01363
01364
01365 if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) {
01366 st->had_vehicle_of_type |= HVOT_AIRCRAFT;
01367 SetDParam(0, st->index);
01368
01369 AddNewsItem(
01370 STR_A033_CITIZENS_CELEBRATE_FIRST,
01371 (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
01372 v->index,
01373 st->index
01374 );
01375 AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
01376 }
01377
01378 v->BeginLoading();
01379 }
01380
01381 static void AircraftLandAirplane(Vehicle *v)
01382 {
01383 v->UpdateDeltaXY(INVALID_DIR);
01384
01385 if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
01386 SndPlayVehicleFx(SND_17_SKID_PLANE, v);
01387 }
01388 MaybeCrashAirplane(v);
01389 }
01390
01391
01393 void AircraftNextAirportPos_and_Order(Vehicle *v)
01394 {
01395 if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) {
01396 v->u.air.targetairport = v->current_order.GetDestination();
01397 }
01398
01399 const Station *st = GetTargetAirportIfValid(v);
01400 const AirportFTAClass *apc = st == NULL ? GetAirport(AT_DUMMY) : st->Airport();
01401 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, apc);
01402 }
01403
01404 void AircraftLeaveHangar(Vehicle *v)
01405 {
01406 v->cur_speed = 0;
01407 v->subspeed = 0;
01408 v->progress = 0;
01409 v->direction = DIR_SE;
01410 v->vehstatus &= ~VS_HIDDEN;
01411 {
01412 Vehicle *u = v->Next();
01413 u->vehstatus &= ~VS_HIDDEN;
01414
01415
01416 u = u->Next();
01417 if (u != NULL) {
01418 u->vehstatus &= ~VS_HIDDEN;
01419 u->cur_speed = 80;
01420 }
01421 }
01422
01423 VehicleServiceInDepot(v);
01424 SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
01425 InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
01426 InvalidateWindowClasses(WC_AIRCRAFT_LIST);
01427 }
01428
01433 static inline bool CheckSendAircraftToHangarForReplacement(const Vehicle *v)
01434 {
01435 EngineID new_engine;
01436 Company *c = GetCompany(v->owner);
01437
01438 if (VehicleHasDepotOrders(v)) return false;
01439
01440 new_engine = EngineReplacementForCompany(c, v->engine_type, v->group_id);
01441
01442 if (new_engine == INVALID_ENGINE) {
01443
01444 new_engine = v->engine_type;
01445
01446 if (!v->NeedsAutorenewing(c)) {
01447
01448 return false;
01449 }
01450 }
01451
01452 if (!HasBit(GetEngine(new_engine)->company_avail, v->owner)) {
01453
01454 return false;
01455 }
01456
01457 if (c->money < (c->engine_renew_money + (2 * DoCommand(0, new_engine, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT).GetCost()))) {
01458
01459
01460
01461
01462 return false;
01463 }
01464
01465
01466 return true;
01467 }
01468
01472 static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc)
01473 {
01474 AircraftEntersTerminal(v);
01475 v->u.air.state = apc->layout[v->u.air.pos].heading;
01476 }
01477
01478 static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc)
01479 {
01480 VehicleEnterDepot(v);
01481 v->u.air.state = apc->layout[v->u.air.pos].heading;
01482 }
01483
01485 static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
01486 {
01487
01488 if (v->u.air.previous_pos != v->u.air.pos) {
01489 AircraftEventHandler_EnterHangar(v, apc);
01490 return;
01491 }
01492
01493
01494 if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
01495 v->current_order.Free();
01496 return;
01497 }
01498
01499 if (!v->current_order.IsType(OT_GOTO_STATION) &&
01500 !v->current_order.IsType(OT_GOTO_DEPOT))
01501 return;
01502
01503
01504 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01505
01506
01507 if (v->current_order.GetDestination() == v->u.air.targetairport) {
01508
01509
01510 if (v->subtype == AIR_HELICOPTER) {
01511 if (!AirportFindFreeHelipad(v, apc)) return;
01512 } else {
01513 if (!AirportFindFreeTerminal(v, apc)) return;
01514 }
01515 } else {
01516
01517 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01518 }
01519 AircraftLeaveHangar(v);
01520 AirportMove(v, apc);
01521 }
01522
01524 static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
01525 {
01526
01527 if (v->u.air.previous_pos != v->u.air.pos) {
01528 AircraftEventHandler_EnterTerminal(v, apc);
01529
01530
01531 if (_settings_game.order.serviceathelipad) {
01532 if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
01533
01534 v->date_of_last_service = _date;
01535 v->breakdowns_since_last_service = 0;
01536 v->reliability = GetEngine(v->engine_type)->reliability;
01537 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01538 }
01539 }
01540 return;
01541 }
01542
01543 if (!v->current_order.IsValid()) return;
01544
01545
01546 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01547
01548
01549
01550
01551 bool go_to_hangar = false;
01552 switch (v->current_order.GetType()) {
01553 case OT_GOTO_STATION:
01554 break;
01555 case OT_GOTO_DEPOT:
01556 go_to_hangar = v->current_order.GetDestination() == v->u.air.targetairport;
01557 break;
01558 case OT_CONDITIONAL:
01559
01560
01561
01562 return;
01563 default:
01564 v->current_order.Free();
01565 go_to_hangar = GetStation(v->u.air.targetairport)->Airport()->nof_depots != 0;
01566 }
01567
01568 if (go_to_hangar) {
01569 v->u.air.state = HANGAR;
01570 } else {
01571
01572 v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
01573 }
01574 AirportMove(v, apc);
01575 }
01576
01577 static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
01578 {
01579 assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
01580 }
01581
01582 static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc)
01583 {
01584 PlayAircraftSound(v);
01585 v->u.air.state = STARTTAKEOFF;
01586 }
01587
01588 static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc)
01589 {
01590 v->u.air.state = ENDTAKEOFF;
01591 v->UpdateDeltaXY(INVALID_DIR);
01592 }
01593
01594 static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc)
01595 {
01596 v->u.air.state = FLYING;
01597
01598 AircraftNextAirportPos_and_Order(v);
01599 }
01600
01601 static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc)
01602 {
01603 v->u.air.state = FLYING;
01604 v->UpdateDeltaXY(INVALID_DIR);
01605
01606
01607 AircraftNextAirportPos_and_Order(v);
01608
01609
01610 if (CheckSendAircraftToHangarForReplacement(v)) {
01611 _current_company = v->owner;
01612 DoCommand(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01613 _current_company = OWNER_NONE;
01614 }
01615 }
01616
01617 static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc)
01618 {
01619 Station *st = GetStation(v->u.air.targetairport);
01620
01621
01622 if (apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES) &&
01623 st->airport_tile != INVALID_TILE &&
01624 (st->owner == OWNER_NONE || st->owner == v->owner)) {
01625
01626
01627
01628 byte landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING;
01629 const AirportFTA *current = apc->layout[v->u.air.pos].next;
01630 while (current != NULL) {
01631 if (current->heading == landingtype) {
01632
01633
01634
01635 uint16 tcur_speed = v->cur_speed;
01636 uint16 tsubspeed = v->subspeed;
01637 if (!AirportHasBlock(v, current, apc)) {
01638 v->u.air.state = landingtype;
01639
01640
01641
01642 v->u.air.pos = current->next_position;
01643 SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block);
01644 return;
01645 }
01646 v->cur_speed = tcur_speed;
01647 v->subspeed = tsubspeed;
01648 }
01649 current = current->next;
01650 }
01651 }
01652 v->u.air.state = FLYING;
01653 v->u.air.pos = apc->layout[v->u.air.pos].next_position;
01654 }
01655
01656 static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc)
01657 {
01658 v->u.air.state = ENDLANDING;
01659 AircraftLandAirplane(v);
01660
01661
01662 if (CheckSendAircraftToHangarForReplacement(v)) {
01663 _current_company = v->owner;
01664 DoCommand(v->tile, v->index, DEPOT_SERVICE, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
01665 _current_company = OWNER_NONE;
01666 }
01667 }
01668
01669 static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc)
01670 {
01671 v->u.air.state = HELIENDLANDING;
01672 v->UpdateDeltaXY(INVALID_DIR);
01673 }
01674
01675 static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc)
01676 {
01677
01678 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01679
01680
01681
01682
01683
01684 if (v->current_order.IsType(OT_GOTO_STATION)) {
01685 if (AirportFindFreeTerminal(v, apc)) return;
01686 }
01687 v->u.air.state = HANGAR;
01688
01689 }
01690
01691 static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc)
01692 {
01693
01694 if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
01695
01696
01697
01698
01699
01700
01701
01702
01703 if (v->current_order.IsType(OT_GOTO_STATION)) {
01704 if (AirportFindFreeHelipad(v, apc)) return;
01705 }
01706 v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF;
01707 }
01708
01709 typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc);
01710 static AircraftStateHandler * const _aircraft_state_handlers[] = {
01711 AircraftEventHandler_General,
01712 AircraftEventHandler_InHangar,
01713 AircraftEventHandler_AtTerminal,
01714 AircraftEventHandler_AtTerminal,
01715 AircraftEventHandler_AtTerminal,
01716 AircraftEventHandler_AtTerminal,
01717 AircraftEventHandler_AtTerminal,
01718 AircraftEventHandler_AtTerminal,
01719 AircraftEventHandler_AtTerminal,
01720 AircraftEventHandler_AtTerminal,
01721 AircraftEventHandler_TakeOff,
01722 AircraftEventHandler_StartTakeOff,
01723 AircraftEventHandler_EndTakeOff,
01724 AircraftEventHandler_HeliTakeOff,
01725 AircraftEventHandler_Flying,
01726 AircraftEventHandler_Landing,
01727 AircraftEventHandler_EndLanding,
01728 AircraftEventHandler_HeliLanding,
01729 AircraftEventHandler_HeliEndLanding,
01730 AircraftEventHandler_AtTerminal,
01731 AircraftEventHandler_AtTerminal,
01732 AircraftEventHandler_AtTerminal,
01733 AircraftEventHandler_AtTerminal,
01734 };
01735
01736 static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc)
01737 {
01738
01739 if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) {
01740 Station *st = GetStation(v->u.air.targetairport);
01741
01742 CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block);
01743 }
01744 }
01745
01746 static void AirportGoToNextPosition(Vehicle *v)
01747 {
01748
01749 if (!AircraftController(v)) return;
01750
01751 const AirportFTAClass *apc = GetStation(v->u.air.targetairport)->Airport();
01752
01753 AirportClearBlock(v, apc);
01754 AirportMove(v, apc);
01755 }
01756
01757
01758 static bool AirportMove(Vehicle *v, const AirportFTAClass *apc)
01759 {
01760
01761 if (v->u.air.pos >= apc->nofelements) {
01762 DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1);
01763 assert(v->u.air.pos < apc->nofelements);
01764 }
01765
01766 const AirportFTA *current = &apc->layout[v->u.air.pos];
01767
01768 if (current->heading == v->u.air.state) {
01769 byte prev_pos = v->u.air.pos;
01770 byte prev_state = v->u.air.state;
01771 _aircraft_state_handlers[v->u.air.state](v, apc);
01772 if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos;
01773 if (v->u.air.state != prev_state || v->u.air.pos != prev_pos) UpdateAircraftCache(v);
01774 return true;
01775 }
01776
01777 v->u.air.previous_pos = v->u.air.pos;
01778
01779
01780 if (current->next == NULL) {
01781 if (AirportSetBlocks(v, current, apc)) {
01782 v->u.air.pos = current->next_position;
01783 UpdateAircraftCache(v);
01784 }
01785 return false;
01786 }
01787
01788
01789
01790 do {
01791 if (v->u.air.state == current->heading || current->heading == TO_ALL) {
01792 if (AirportSetBlocks(v, current, apc)) {
01793 v->u.air.pos = current->next_position;
01794 UpdateAircraftCache(v);
01795 }
01796 return false;
01797 }
01798 current = current->next;
01799 } while (current != NULL);
01800
01801 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);
01802 assert(0);
01803 return false;
01804 }
01805
01806
01807 static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01808 {
01809 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01810 const AirportFTA *next = &apc->layout[current_pos->next_position];
01811
01812
01813 if (apc->layout[current_pos->position].block != next->block) {
01814 const Station *st = GetStation(v->u.air.targetairport);
01815 uint64 airport_flags = next->block;
01816
01817
01818 if (current_pos != reference && current_pos->block != NOTHING_block) {
01819 airport_flags |= current_pos->block;
01820 }
01821
01822 if (HASBITS(st->airport_flags, airport_flags)) {
01823 v->cur_speed = 0;
01824 v->subspeed = 0;
01825 return true;
01826 }
01827 }
01828 return false;
01829 }
01830
01838 static bool AirportSetBlocks(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc)
01839 {
01840 const AirportFTA *next = &apc->layout[current_pos->next_position];
01841 const AirportFTA *reference = &apc->layout[v->u.air.pos];
01842
01843
01844 if ((apc->layout[current_pos->position].block & next->block) != next->block) {
01845 uint64 airport_flags = next->block;
01846
01847
01848 const AirportFTA *current = current_pos;
01849 if (current == reference) current = current->next;
01850 while (current != NULL) {
01851 if (current->heading == current_pos->heading && current->block != 0) {
01852 airport_flags |= current->block;
01853 break;
01854 }
01855 current = current->next;
01856 };
01857
01858
01859
01860 if (current_pos->block == next->block) airport_flags ^= next->block;
01861
01862 Station *st = GetStation(v->u.air.targetairport);
01863 if (HASBITS(st->airport_flags, airport_flags)) {
01864 v->cur_speed = 0;
01865 v->subspeed = 0;
01866 return false;
01867 }
01868
01869 if (next->block != NOTHING_block) {
01870 SETBITS(st->airport_flags, airport_flags);
01871 }
01872 }
01873 return true;
01874 }
01875
01876 static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal)
01877 {
01878 Station *st = GetStation(v->u.air.targetairport);
01879 for (; i < last_terminal; i++) {
01880 if (!HasBit(st->airport_flags, _airport_terminal_flag[i])) {
01881
01882 v->u.air.state = _airport_terminal_state[i];
01883 SetBit(st->airport_flags, _airport_terminal_flag[i]);
01884 return true;
01885 }
01886 }
01887 return false;
01888 }
01889
01890 static uint GetNumTerminals(const AirportFTAClass *apc)
01891 {
01892 uint num = 0;
01893
01894 for (uint i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i];
01895
01896 return num;
01897 }
01898
01899 static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc)
01900 {
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911 if (apc->terminals[0] > 1) {
01912 const Station *st = GetStation(v->u.air.targetairport);
01913 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01914
01915 while (temp != NULL) {
01916 if (temp->heading == 255) {
01917 if (!HASBITS(st->airport_flags, temp->block)) {
01918
01919
01920 uint target_group = temp->next_position + 1;
01921
01922
01923
01924
01925 uint group_start = 0;
01926 for (uint i = 1; i < target_group; i++) {
01927 group_start += apc->terminals[i];
01928 }
01929
01930 uint group_end = group_start + apc->terminals[target_group];
01931 if (FreeTerminal(v, group_start, group_end)) return true;
01932 }
01933 } else {
01934
01935
01936 return false;
01937 }
01938 temp = temp->next;
01939 }
01940 }
01941
01942
01943 return FreeTerminal(v, 0, GetNumTerminals(apc));
01944 }
01945
01946 static uint GetNumHelipads(const AirportFTAClass *apc)
01947 {
01948 uint num = 0;
01949
01950 for (uint i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i];
01951
01952 return num;
01953 }
01954
01955
01956 static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc)
01957 {
01958
01959 if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc);
01960
01961
01962 if (apc->helipads[0] > 1) {
01963 const Station *st = GetStation(v->u.air.targetairport);
01964 const AirportFTA *temp = apc->layout[v->u.air.pos].next;
01965
01966 while (temp != NULL) {
01967 if (temp->heading == 255) {
01968 if (!HASBITS(st->airport_flags, temp->block)) {
01969
01970
01971
01972 uint target_group = temp->next_position + 1;
01973
01974
01975
01976
01977 uint group_start = 0;
01978 for (uint i = 1; i < target_group; i++) {
01979 group_start += apc->helipads[i];
01980 }
01981
01982 uint group_end = group_start + apc->helipads[target_group];
01983 if (FreeTerminal(v, group_start, group_end)) return true;
01984 }
01985 } else {
01986
01987
01988 return false;
01989 }
01990 temp = temp->next;
01991 }
01992 } else {
01993
01994
01995 return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS);
01996 }
01997 return false;
01998 }
01999
02000 static void AircraftEventHandler(Vehicle *v, int loop)
02001 {
02002 v->tick_counter++;
02003
02004 if (v->vehstatus & VS_CRASHED) {
02005 HandleCrashedAircraft(v);
02006 return;
02007 }
02008
02009 if (v->vehstatus & VS_STOPPED) return;
02010
02011
02012 if (v->breakdown_ctr != 0) {
02013 if (v->breakdown_ctr <= 2) {
02014 HandleBrokenAircraft(v);
02015 } else {
02016 if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
02017 }
02018 }
02019
02020 HandleAircraftSmoke(v);
02021 ProcessOrders(v);
02022 v->HandleLoading(loop != 0);
02023
02024 if (v->current_order.IsType(OT_LOADING) || v->current_order.IsType(OT_LEAVESTATION)) return;
02025
02026 AirportGoToNextPosition(v);
02027 }
02028
02029 void Aircraft::Tick()
02030 {
02031 if (!IsNormalAircraft(this)) return;
02032
02033 if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
02034
02035 if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this);
02036
02037 AgeAircraftCargo(this);
02038
02039 this->current_order_time++;
02040
02041 for (uint i = 0; i != 2; i++) {
02042 AircraftEventHandler(this, i);
02043 if (this->type != VEH_AIRCRAFT)
02044 break;
02045 }
02046 }
02047
02048
02054 Station *GetTargetAirportIfValid(const Vehicle *v)
02055 {
02056 assert(v->type == VEH_AIRCRAFT);
02057
02058 StationID sid = v->u.air.targetairport;
02059
02060 if (!IsValidStationID(sid)) return NULL;
02061
02062 Station *st = GetStation(sid);
02063
02064 return st->airport_tile == INVALID_TILE ? NULL : st;
02065 }
02066
02071 void UpdateAirplanesOnNewStation(const Station *st)
02072 {
02073
02074 const AirportFTAClass *ap = st->Airport();
02075
02076 Vehicle *v;
02077 FOR_ALL_VEHICLES(v) {
02078 if (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) {
02079 if (v->u.air.targetairport == st->index) {
02080
02081
02082 if (v->u.air.state >= FLYING) {
02083 v->u.air.pos = v->u.air.previous_pos = AircraftGetEntryPoint(v, ap);
02084 v->u.air.state = FLYING;
02085 UpdateAircraftCache(v);
02086
02087
02088 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
02089
02090 SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v));
02091 } else {
02092 assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF);
02093 byte takeofftype = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : ENDTAKEOFF;
02094
02095
02096 for (uint cnt = 0; cnt < ap->nofelements; cnt++) {
02097 if (ap->layout[cnt].heading == takeofftype) {
02098 v->u.air.pos = ap->layout[cnt].position;
02099 UpdateAircraftCache(v);
02100 break;
02101 }
02102 }
02103 }
02104 }
02105 }
02106 }
02107 }