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