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