|
@@ -242,97 +242,97 @@ static CommandCost EstimateAircraftCost(
|
|
|
uint16 AircraftDefaultCargoCapacity(CargoID cid, const AircraftVehicleInfo *avi)
|
|
|
{
|
|
|
assert(cid != CT_INVALID);
|
|
|
|
|
|
/* An aircraft can carry twice as much goods as normal cargo,
|
|
|
* and four times as many passengers. */
|
|
|
switch (cid) {
|
|
|
case CT_PASSENGERS:
|
|
|
return avi->passenger_capacity;
|
|
|
case CT_MAIL:
|
|
|
return avi->passenger_capacity + avi->mail_capacity;
|
|
|
case CT_GOODS:
|
|
|
return (avi->passenger_capacity + avi->mail_capacity) / 2;
|
|
|
default:
|
|
|
return (avi->passenger_capacity + avi->mail_capacity) / 4;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** Build an aircraft.
|
|
|
* @param tile tile of depot where aircraft is built
|
|
|
* @param flags for command
|
|
|
* @param p1 aircraft type being built (engine)
|
|
|
* @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number
|
|
|
* return result of operation. Could be cost, error
|
|
|
*/
|
|
|
CommandCost CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
|
|
{
|
|
|
if (!IsEngineBuildable(p1, VEH_AIRCRAFT, _current_player)) return_cmd_error(STR_AIRCRAFT_NOT_AVAILABLE);
|
|
|
|
|
|
const AircraftVehicleInfo *avi = AircraftVehInfo(p1);
|
|
|
CommandCost value = EstimateAircraftCost(p1, avi);
|
|
|
|
|
|
/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
|
|
|
if (flags & DC_QUERY_COST) return value;
|
|
|
|
|
|
if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR;
|
|
|
|
|
|
/* Prevent building aircraft types at places which can't handle them */
|
|
|
if (!CanAircraftUseStation(p1, tile)) return CMD_ERROR;
|
|
|
|
|
|
/* Allocate 2 or 3 vehicle structs, depending on type
|
|
|
* vl[0] = aircraft, vl[1] = shadow, [vl[2] = rotor] */
|
|
|
Vehicle *vl[3];
|
|
|
if (!Vehicle::AllocateList(vl, avi->subtype & AIR_CTOL ? 2 : 3)) {
|
|
|
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
|
|
|
}
|
|
|
|
|
|
UnitID unit_num = HasBit(p2, 0) ? 0 : GetFreeUnitNumber(VEH_AIRCRAFT);
|
|
|
if (unit_num > _settings.vehicle.max_aircraft)
|
|
|
if (unit_num > _settings_game.vehicle.max_aircraft)
|
|
|
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
Vehicle *v = vl[0]; // aircraft
|
|
|
Vehicle *u = vl[1]; // shadow
|
|
|
|
|
|
v = new (v) Aircraft();
|
|
|
u = new (u) Aircraft();
|
|
|
v->unitnumber = unit_num;
|
|
|
v->direction = DIR_SE;
|
|
|
|
|
|
v->owner = u->owner = _current_player;
|
|
|
|
|
|
v->tile = tile;
|
|
|
// u->tile = 0;
|
|
|
|
|
|
uint x = TileX(tile) * TILE_SIZE + 5;
|
|
|
uint y = TileY(tile) * TILE_SIZE + 3;
|
|
|
|
|
|
v->x_pos = u->x_pos = x;
|
|
|
v->y_pos = u->y_pos = y;
|
|
|
|
|
|
u->z_pos = GetSlopeZ(x, y);
|
|
|
v->z_pos = u->z_pos + 1;
|
|
|
|
|
|
v->running_ticks = 0;
|
|
|
|
|
|
// u->delta_x = u->delta_y = 0;
|
|
|
|
|
|
v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
|
|
|
u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW;
|
|
|
|
|
|
v->spritenum = avi->image_index;
|
|
|
// v->cargo_count = u->number_of_pieces = 0;
|
|
|
|
|
|
v->cargo_cap = avi->passenger_capacity;
|
|
|
u->cargo_cap = avi->mail_capacity;
|
|
|
|
|
|
v->cargo_type = CT_PASSENGERS;
|
|
|
u->cargo_type = CT_MAIL;
|
|
|
|
|
|
v->cargo_subtype = 0;
|
|
|
|
|
|
v->name = NULL;
|
|
|
// v->next_order_param = v->next_order = 0;
|
|
|
|
|
|
// v->load_unload_time_rem = 0;
|
|
|
// v->progress = 0;
|
|
@@ -359,97 +359,97 @@ CommandCost CmdBuildAircraft(TileIndex t
|
|
|
if (cargo != CT_INVALID && cargo != CT_PASSENGERS) {
|
|
|
uint16 callback = CALLBACK_FAILED;
|
|
|
|
|
|
v->cargo_type = cargo;
|
|
|
|
|
|
if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
|
|
|
callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
|
|
|
}
|
|
|
|
|
|
if (callback == CALLBACK_FAILED) {
|
|
|
/* Callback failed, or not executed; use the default cargo capacity */
|
|
|
v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, avi);
|
|
|
} else {
|
|
|
v->cargo_cap = callback;
|
|
|
}
|
|
|
|
|
|
/* Set the 'second compartent' capacity to none */
|
|
|
u->cargo_cap = 0;
|
|
|
}
|
|
|
|
|
|
const Engine *e = GetEngine(p1);
|
|
|
v->reliability = e->reliability;
|
|
|
v->reliability_spd_dec = e->reliability_spd_dec;
|
|
|
v->max_age = e->lifelength * 366;
|
|
|
|
|
|
_new_vehicle_id = v->index;
|
|
|
|
|
|
/* When we click on hangar we know the tile it is on. By that we know
|
|
|
* its position in the array of depots the airport has.....we can search
|
|
|
* layout for #th position of depot. Since layout must start with a listing
|
|
|
* of all depots, it is simple */
|
|
|
for (uint i = 0;; i++) {
|
|
|
const Station *st = GetStationByTile(tile);
|
|
|
const AirportFTAClass *apc = st->Airport();
|
|
|
|
|
|
assert(i != apc->nof_depots);
|
|
|
if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) {
|
|
|
assert(apc->layout[i].heading == HANGAR);
|
|
|
v->u.air.pos = apc->layout[i].position;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
v->u.air.state = HANGAR;
|
|
|
v->u.air.previous_pos = v->u.air.pos;
|
|
|
v->u.air.targetairport = GetStationIndex(tile);
|
|
|
v->SetNext(u);
|
|
|
|
|
|
v->service_interval = _settings.vehicle.servint_aircraft;
|
|
|
v->service_interval = _settings_game.vehicle.servint_aircraft;
|
|
|
|
|
|
v->date_of_last_service = _date;
|
|
|
v->build_year = u->build_year = _cur_year;
|
|
|
|
|
|
v->cur_image = u->cur_image = 0xEA0;
|
|
|
|
|
|
v->random_bits = VehicleRandomBits();
|
|
|
u->random_bits = VehicleRandomBits();
|
|
|
|
|
|
v->vehicle_flags = 0;
|
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
|
|
|
|
|
|
UpdateAircraftCache(v);
|
|
|
|
|
|
VehiclePositionChanged(v);
|
|
|
VehiclePositionChanged(u);
|
|
|
|
|
|
/* Aircraft with 3 vehicles (chopper)? */
|
|
|
if (v->subtype == AIR_HELICOPTER) {
|
|
|
Vehicle *w = vl[2];
|
|
|
|
|
|
w = new (w) Aircraft();
|
|
|
w->direction = DIR_N;
|
|
|
w->owner = _current_player;
|
|
|
w->x_pos = v->x_pos;
|
|
|
w->y_pos = v->y_pos;
|
|
|
w->z_pos = v->z_pos + 5;
|
|
|
w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
|
|
|
w->spritenum = 0xFF;
|
|
|
w->subtype = AIR_ROTOR;
|
|
|
w->cur_image = SPR_ROTOR_STOPPED;
|
|
|
w->random_bits = VehicleRandomBits();
|
|
|
/* Use rotor's air.state to store the rotor animation frame */
|
|
|
w->u.air.state = HRS_ROTOR_STOPPED;
|
|
|
w->UpdateDeltaXY(INVALID_DIR);
|
|
|
|
|
|
u->SetNext(w);
|
|
|
VehiclePositionChanged(w);
|
|
|
}
|
|
|
|
|
|
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
|
|
InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
|
|
|
InvalidateWindow(WC_COMPANY, v->owner);
|
|
|
if (IsLocalPlayer())
|
|
|
InvalidateAutoreplaceWindow(v->engine_type, v->group_id); //updates the replace Aircraft window
|
|
|
|
|
|
GetPlayer(_current_player)->num_engines[p1]++;
|
|
|
}
|
|
@@ -619,97 +619,97 @@ CommandCost CmdRefitAircraft(TileIndex t
|
|
|
v->cargo_type = new_cid;
|
|
|
v->cargo_subtype = new_subtype;
|
|
|
|
|
|
callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
|
|
|
|
|
|
/* Restore the cargo type */
|
|
|
v->cargo_type = temp_cid;
|
|
|
v->cargo_subtype = temp_subtype;
|
|
|
}
|
|
|
|
|
|
const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
|
|
|
|
|
|
uint pass;
|
|
|
if (callback == CALLBACK_FAILED) {
|
|
|
/* If the callback failed, or wasn't executed, use the aircraft's
|
|
|
* default cargo capacity */
|
|
|
pass = AircraftDefaultCargoCapacity(new_cid, avi);
|
|
|
} else {
|
|
|
pass = callback;
|
|
|
}
|
|
|
_returned_refit_capacity = pass;
|
|
|
|
|
|
CommandCost cost;
|
|
|
if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
|
|
|
cost = GetRefitCost(v->engine_type);
|
|
|
}
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
v->cargo_cap = pass;
|
|
|
|
|
|
Vehicle *u = v->Next();
|
|
|
uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
|
|
|
u->cargo_cap = mail;
|
|
|
v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
|
|
|
u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
|
|
|
v->cargo_type = new_cid;
|
|
|
v->cargo_subtype = new_subtype;
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
|
|
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
|
|
|
InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
|
|
|
}
|
|
|
|
|
|
return cost;
|
|
|
}
|
|
|
|
|
|
|
|
|
static void CheckIfAircraftNeedsService(Vehicle *v)
|
|
|
{
|
|
|
if (_settings.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
|
|
|
if (_settings_game.vehicle.servint_aircraft == 0 || !v->NeedsAutomaticServicing()) return;
|
|
|
if (v->IsInDepot()) {
|
|
|
VehicleServiceInDepot(v);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const Station *st = GetStation(v->current_order.GetDestination());
|
|
|
/* only goto depot if the target airport has terminals (eg. it is airport) */
|
|
|
if (st->IsValid() && st->airport_tile != 0 && st->Airport()->terminals != NULL) {
|
|
|
// printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
|
|
|
// v->u.air.targetairport = st->index;
|
|
|
v->current_order.MakeGoToDepot(st->index, ODTFB_SERVICE);
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
} else if (v->current_order.IsType(OT_GOTO_DEPOT)) {
|
|
|
v->current_order.MakeDummy();
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void Aircraft::OnNewDay()
|
|
|
{
|
|
|
if (!IsNormalAircraft(this)) return;
|
|
|
|
|
|
if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
|
|
|
|
|
|
CheckOrders(this);
|
|
|
|
|
|
CheckVehicleBreakdown(this);
|
|
|
AgeVehicle(this);
|
|
|
CheckIfAircraftNeedsService(this);
|
|
|
|
|
|
if (this->running_ticks == 0) return;
|
|
|
|
|
|
CommandCost cost(EXPENSES_AIRCRAFT_RUN, GetVehicleProperty(this, 0x0E, AircraftVehInfo(this->engine_type)->running_cost) * _price.aircraft_running * this->running_ticks / (364 * DAY_TICKS));
|
|
|
|
|
|
this->profit_this_year -= cost.GetCost();
|
|
|
this->running_ticks = 0;
|
|
|
|
|
|
SubtractMoneyFromPlayerFract(this->owner, cost);
|
|
|
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
|
|
|
InvalidateWindowClasses(WC_AIRCRAFT_LIST);
|
|
|
}
|
|
|
|
|
|
void AircraftYearlyLoop()
|
|
|
{
|
|
|
Vehicle *v;
|
|
|
|
|
|
FOR_ALL_VEHICLES(v) {
|
|
@@ -839,131 +839,131 @@ void HandleAircraftEnterHangar(Vehicle *
|
|
|
|
|
|
static void PlayAircraftSound(const Vehicle* v)
|
|
|
{
|
|
|
if (!PlayVehicleSound(v, VSE_START)) {
|
|
|
SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
void UpdateAircraftCache(Vehicle *v)
|
|
|
{
|
|
|
uint max_speed = GetVehicleProperty(v, 0x0C, 0);
|
|
|
if (max_speed != 0) {
|
|
|
/* Convert from original units to (approx) km/h */
|
|
|
max_speed = (max_speed * 129) / 10;
|
|
|
|
|
|
v->u.air.cached_max_speed = max_speed;
|
|
|
} else {
|
|
|
v->u.air.cached_max_speed = 0xFFFF;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Special velocities for aircraft
|
|
|
*/
|
|
|
enum AircraftSpeedLimits {
|
|
|
SPEED_LIMIT_TAXI = 50, ///< Maximum speed of an aircraft while taxiing
|
|
|
SPEED_LIMIT_APPROACH = 230, ///< Maximum speed of an aircraft on finals
|
|
|
SPEED_LIMIT_BROKEN = 320, ///< Maximum speed of an aircraft that is broken
|
|
|
SPEED_LIMIT_HOLD = 425, ///< Maximum speed of an aircraft that flies the holding pattern
|
|
|
SPEED_LIMIT_NONE = 0xFFFF ///< No environmental speed limit. Speed limit is type dependent
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* Sets the new speed for an aircraft
|
|
|
* @param v The vehicle for which the speed should be obtained
|
|
|
* @param speed_limit The maximum speed the vehicle may have.
|
|
|
* @param hard_limit If true, the limit is directly enforced, otherwise the plane is slowed down gradually
|
|
|
* @return The number of position updates needed within the tick
|
|
|
*/
|
|
|
static int UpdateAircraftSpeed(Vehicle *v, uint speed_limit = SPEED_LIMIT_NONE, bool hard_limit = true)
|
|
|
{
|
|
|
uint spd = v->acceleration * 16;
|
|
|
byte t;
|
|
|
|
|
|
/* Adjust speed limits by plane speed factor to prevent taxiing
|
|
|
* and take-off speeds being too low. */
|
|
|
speed_limit *= _settings.vehicle.plane_speed;
|
|
|
speed_limit *= _settings_game.vehicle.plane_speed;
|
|
|
|
|
|
if (v->u.air.cached_max_speed < speed_limit) {
|
|
|
if (v->cur_speed < speed_limit) hard_limit = false;
|
|
|
speed_limit = v->u.air.cached_max_speed;
|
|
|
}
|
|
|
|
|
|
speed_limit = min(speed_limit, v->max_speed);
|
|
|
|
|
|
v->subspeed = (t=v->subspeed) + (byte)spd;
|
|
|
|
|
|
/* Aircraft's current speed is used twice so that very fast planes are
|
|
|
* forced to slow down rapidly in the short distance needed. The magic
|
|
|
* value 16384 was determined to give similar results to the old speed/48
|
|
|
* method at slower speeds. This also results in less reduction at slow
|
|
|
* speeds to that aircraft do not get to taxi speed straight after
|
|
|
* touchdown. */
|
|
|
if (!hard_limit && v->cur_speed > speed_limit) {
|
|
|
speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings.vehicle.plane_speed);
|
|
|
speed_limit = v->cur_speed - max(1, ((v->cur_speed * v->cur_speed) / 16384) / _settings_game.vehicle.plane_speed);
|
|
|
}
|
|
|
|
|
|
spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), speed_limit);
|
|
|
|
|
|
/* adjust speed for broken vehicles */
|
|
|
if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, SPEED_LIMIT_BROKEN);
|
|
|
|
|
|
/* updates statusbar only if speed have changed to save CPU time */
|
|
|
if (spd != v->cur_speed) {
|
|
|
v->cur_speed = spd;
|
|
|
if (_settings.gui.vehicle_speed)
|
|
|
if (_settings_client.gui.vehicle_speed)
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
|
|
|
/* Adjust distance moved by plane speed setting */
|
|
|
if (_settings.vehicle.plane_speed > 1) spd /= _settings.vehicle.plane_speed;
|
|
|
if (_settings_game.vehicle.plane_speed > 1) spd /= _settings_game.vehicle.plane_speed;
|
|
|
|
|
|
if (!(v->direction & 1)) spd = spd * 3 / 4;
|
|
|
|
|
|
spd += v->progress;
|
|
|
v->progress = (byte)spd;
|
|
|
return spd >> 8;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Gets the cruise altitude of an aircraft.
|
|
|
* The cruise altitude is determined by the velocity of the vehicle
|
|
|
* and the direction it is moving
|
|
|
* @param v The vehicle. Should be an aircraft
|
|
|
* @returns Altitude in pixel units
|
|
|
*/
|
|
|
static byte GetAircraftFlyingAltitude(const Vehicle *v)
|
|
|
{
|
|
|
/* Make sure Aircraft fly no lower so that they don't conduct
|
|
|
* CFITs (controlled flight into terrain)
|
|
|
*/
|
|
|
byte base_altitude = 150;
|
|
|
|
|
|
/* Make sure eastbound and westbound planes do not "crash" into each
|
|
|
* other by providing them with vertical seperation
|
|
|
*/
|
|
|
switch (v->direction) {
|
|
|
case DIR_N:
|
|
|
case DIR_NE:
|
|
|
case DIR_E:
|
|
|
case DIR_SE:
|
|
|
base_altitude += 10;
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
}
|
|
|
|
|
|
/* Make faster planes fly higher so that they can overtake slower ones */
|
|
|
base_altitude += min(20 * (v->max_speed / 200), 90);
|
|
|
|
|
|
return base_altitude;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Find the entry point to an airport depending on direction which
|
|
|
* the airport is being approached from. Each airport can have up to
|
|
|
* four entry points for its approach system so that approaching
|
|
|
* aircraft do not fly through each other or are forced to do 180
|
|
|
* degree turns during the approach. The arrivals are grouped into
|
|
@@ -1554,97 +1554,97 @@ static void AircraftEventHandler_EnterHa
|
|
|
|
|
|
/** In an Airport Hangar */
|
|
|
static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc)
|
|
|
{
|
|
|
/* if we just arrived, execute EnterHangar first */
|
|
|
if (v->u.air.previous_pos != v->u.air.pos) {
|
|
|
AircraftEventHandler_EnterHangar(v, apc);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
/* if we were sent to the depot, stay there */
|
|
|
if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
|
|
|
v->current_order.Free();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!v->current_order.IsType(OT_GOTO_STATION) &&
|
|
|
!v->current_order.IsType(OT_GOTO_DEPOT))
|
|
|
return;
|
|
|
|
|
|
/* if the block of the next position is busy, stay put */
|
|
|
if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
|
|
|
|
|
|
/* We are already at the target airport, we need to find a terminal */
|
|
|
if (v->current_order.GetDestination() == v->u.air.targetairport) {
|
|
|
/* FindFreeTerminal:
|
|
|
* 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal */
|
|
|
if (v->subtype == AIR_HELICOPTER) {
|
|
|
if (!AirportFindFreeHelipad(v, apc)) return; // helicopter
|
|
|
} else {
|
|
|
if (!AirportFindFreeTerminal(v, apc)) return; // airplane
|
|
|
}
|
|
|
} else { // Else prepare for launch.
|
|
|
/* airplane goto state takeoff, helicopter to helitakeoff */
|
|
|
v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
|
|
|
}
|
|
|
AircraftLeaveHangar(v);
|
|
|
AirportMove(v, apc);
|
|
|
}
|
|
|
|
|
|
/** At one of the Airport's Terminals */
|
|
|
static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc)
|
|
|
{
|
|
|
/* if we just arrived, execute EnterTerminal first */
|
|
|
if (v->u.air.previous_pos != v->u.air.pos) {
|
|
|
AircraftEventHandler_EnterTerminal(v, apc);
|
|
|
/* on an airport with helipads, a helicopter will always land there
|
|
|
* and get serviced at the same time - patch setting */
|
|
|
if (_settings.order.serviceathelipad) {
|
|
|
if (_settings_game.order.serviceathelipad) {
|
|
|
if (v->subtype == AIR_HELICOPTER && apc->helipads != NULL) {
|
|
|
/* an exerpt of ServiceAircraft, without the invisibility stuff */
|
|
|
v->date_of_last_service = _date;
|
|
|
v->breakdowns_since_last_service = 0;
|
|
|
v->reliability = GetEngine(v->engine_type)->reliability;
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!v->current_order.IsValid()) return;
|
|
|
|
|
|
/* if the block of the next position is busy, stay put */
|
|
|
if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return;
|
|
|
|
|
|
/* airport-road is free. We either have to go to another airport, or to the hangar
|
|
|
* ---> start moving */
|
|
|
|
|
|
switch (v->current_order.GetType()) {
|
|
|
case OT_GOTO_STATION: // ready to fly to another airport
|
|
|
/* airplane goto state takeoff, helicopter to helitakeoff */
|
|
|
v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
|
|
|
break;
|
|
|
case OT_GOTO_DEPOT: // visit hangar for serivicing, sale, etc.
|
|
|
if (v->current_order.GetDestination() == v->u.air.targetairport) {
|
|
|
v->u.air.state = HANGAR;
|
|
|
} else {
|
|
|
v->u.air.state = (v->subtype == AIR_HELICOPTER) ? HELITAKEOFF : TAKEOFF;
|
|
|
}
|
|
|
break;
|
|
|
default: // orders have been deleted (no orders), goto depot and don't bother us
|
|
|
v->current_order.Free();
|
|
|
v->u.air.state = HANGAR;
|
|
|
}
|
|
|
AirportMove(v, apc);
|
|
|
}
|
|
|
|
|
|
static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc)
|
|
|
{
|
|
|
assert("OK, you shouldn't be here, check your Airport Scheme!" && 0);
|
|
|
}
|
|
|
|
|
|
static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc)
|
|
|
{
|
|
|
PlayAircraftSound(v); // play takeoffsound for airplanes
|
|
|
v->u.air.state = STARTTAKEOFF;
|
|
|
}
|