Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
307 lines
20 KiB
C
307 lines
20 KiB
C
#ifdef WORKBENCH
|
|
// Arranged local native fixture: protects test aircraft and crew, directly
|
|
// dispatches production rescue, and passively measures native landing times.
|
|
// No simulation/compartment override is used by the landing/boarding flow.
|
|
modded class ERR_RescueFlightComponent
|
|
{
|
|
protected float m_ERRMeasuredLanding = -1, m_ERRMeasuredReady = -1, m_ERRMeasuredLanded = -1, m_ERRMeasureLog;
|
|
protected float m_ERRMaximumDescent, m_ERRMaximumLateral, m_ERRMaximumTilt;
|
|
override void Update(float now)
|
|
{
|
|
super.Update(now);
|
|
if (m_Escort || m_Departing || !m_Aircraft || m_Phase < ERR_ReconPhase.LANDING) return;
|
|
if (m_ERRMeasuredLanding < 0)
|
|
{
|
|
m_ERRMeasuredLanding = m_LandingStarted;
|
|
PrintFormat("ERR RESCUE LANDING START serial=%1 time=%2 agl=%3 horizontal=%4 prefab=%5", m_Serial, m_ERRMeasuredLanding, m_Aircraft.GetOrigin()[1] - SCR_TerrainHelper.GetTerrainY(m_Aircraft.GetOrigin(), GetGame().GetWorld(), noUnderwater: true), vector.DistanceXZ(m_Aircraft.GetOrigin(), LandingTarget()), m_Aircraft.GetPrefabData().GetPrefabName());
|
|
}
|
|
Physics physics = m_Aircraft.GetPhysics();
|
|
if (!physics) return;
|
|
vector velocity = physics.GetVelocity();
|
|
vector angles = m_Aircraft.GetYawPitchRoll();
|
|
vector horizontal = velocity; horizontal[1] = 0;
|
|
m_ERRMaximumDescent = Math.Max(m_ERRMaximumDescent, -velocity[1]);
|
|
m_ERRMaximumLateral = Math.Max(m_ERRMaximumLateral, horizontal.Length());
|
|
m_ERRMaximumTilt = Math.Max(m_ERRMaximumTilt, Math.Max(Math.AbsFloat(angles[1]), Math.AbsFloat(angles[2])));
|
|
bool ready = ERR_CasualtyLoadingRules.AircraftReady(m_Aircraft);
|
|
if (ready && m_ERRMeasuredReady < 0) { m_ERRMeasuredReady = now; ERR_FixtureLandingTelemetry(now, "physical-ready"); }
|
|
if (now >= m_ERRMeasureLog && m_ERRMeasuredLanded < 0) { m_ERRMeasureLog = now + 1000; ERR_FixtureLandingTelemetry(now, "flight"); }
|
|
}
|
|
override bool Landed()
|
|
{
|
|
bool landed = super.Landed();
|
|
if (landed && m_ERRMeasuredLanded < 0 && m_ERRMeasuredLanding >= 0)
|
|
{
|
|
float now = GetGame().GetWorld().GetWorldTime();
|
|
m_ERRMeasuredLanded = now; ERR_FixtureLandingTelemetry(now, "boardable");
|
|
PrintFormat("ERR RESCUE LANDING RESULT serial=%1 landingSeconds=%2 physicalReadySeconds=%3 maxSampledDescent=%4 maxSampledLateral=%5 maxSampledTiltDegrees=%6", m_Serial, (now - m_ERRMeasuredLanding) / 1000, (m_ERRMeasuredReady - m_ERRMeasuredLanding) / 1000, m_ERRMaximumDescent, m_ERRMaximumLateral, m_ERRMaximumTilt);
|
|
}
|
|
return landed;
|
|
}
|
|
void ERR_FixtureLandingTelemetry(float now, string sampleKind)
|
|
{
|
|
Physics physics = m_Aircraft.GetPhysics();
|
|
HelicopterControllerComponent controller = HelicopterControllerComponent.Cast(m_Aircraft.FindComponent(HelicopterControllerComponent));
|
|
VehicleHelicopterSimulation simulation; if (controller) simulation = controller.GetSimulation();
|
|
vector transform[4]; m_Aircraft.GetWorldTransform(transform);
|
|
PrintFormat("ERR RESCUE LANDING SAMPLE serial=%1 event=%2 elapsed=%3 time=%4 position=%5 velocity=%6 angular=%7 up=%8 phase=%9", m_Serial, sampleKind, (now - m_ERRMeasuredLanding) / 1000, now, m_Aircraft.GetOrigin(), physics.GetVelocity(), physics.GetAngularVelocity(), transform[1][1], Phase());
|
|
PrintFormat("ERR RESCUE LANDING SUPPORT serial=%1 event=%2 elapsed=%3 nativeContact=%4 support=%5 awake=%6 engine=%7 ready=%8", m_Serial, sampleKind, (now - m_ERRMeasuredLanding) / 1000, simulation && simulation.HasAnyGroundContact(), ERR_CasualtyGroundSupport.Parked(m_Aircraft), physics.IsActive(), simulation && simulation.EngineIsOn(), ERR_CasualtyLoadingRules.AircraftReady(m_Aircraft));
|
|
}
|
|
int ERR_FixtureCargo() { return CargoCapacity(m_Aircraft); }
|
|
void ERR_FixtureProtect()
|
|
{
|
|
DamageManagerComponent damage = DamageManagerComponent.Cast(m_Aircraft.FindComponent(DamageManagerComponent));
|
|
if (damage) damage.EnableDamageHandling(false);
|
|
foreach (IEntity actor : m_OriginalCrew)
|
|
{
|
|
if (!actor) continue;
|
|
damage = DamageManagerComponent.Cast(actor.FindComponent(DamageManagerComponent));
|
|
if (damage) damage.EnableDamageHandling(false);
|
|
}
|
|
}
|
|
BaseCompartmentSlot ERR_FixturePassengerSeat()
|
|
{
|
|
array<BaseCompartmentSlot> seats = {}; CargoSeats(m_Aircraft, seats);
|
|
if (seats.IsEmpty()) return null;
|
|
return seats[0];
|
|
}
|
|
}
|
|
modded class ERR_CasualtyLoadingService
|
|
{
|
|
BaseCompartmentSlot ERR_FixtureAvailableSeat(IEntity patient, Vehicle aircraft) { return AvailableSeat(patient, aircraft); }
|
|
}
|
|
modded class ERR_ExtractionService
|
|
{
|
|
protected override bool Approach(out vector position)
|
|
{
|
|
string scope; System.GetCLIParam("errRescueFixtureScope", scope);
|
|
if (scope != "flight-short") return super.Approach(position);
|
|
position = ERR_Actors.Ground(m_Pickup + "400 0 0") + "0 80 0";
|
|
PrintFormat("ERR RESCUE FACTION ARRANGED: shortened initial approach origin=%1; native flight and landing untouched", position);
|
|
return true;
|
|
}
|
|
void ERR_FixtureDispatch() { m_Signal = true; }
|
|
bool ERR_FixturePads() { return LandingPads(); }
|
|
bool ERR_FixtureAirportPickup(vector position)
|
|
{
|
|
if (!ERR_AirfieldPlacement.ClearGround(position)) return false;
|
|
m_Pickup = position; m_TransportPickups.Clear(); m_TransportPickups.Insert(position);
|
|
return LandingPads();
|
|
}
|
|
}
|
|
modded class ERR_GameMode
|
|
{
|
|
protected string m_ERRFixtureFactionId, m_ERRFixtureFactionKey;
|
|
protected int m_ERRFixtureTransportCount;
|
|
protected int m_ERRFixtureStep;
|
|
protected string m_ERRFixtureScope;
|
|
protected float m_ERRFixtureDeadline, m_ERRFixtureWait, m_ERRFixtureNextLog;
|
|
protected float m_ERRFixtureDepartureDeadline, m_ERRFixtureDepartureLog;
|
|
protected IEntity m_ERRFixturePlayer, m_ERRFixtureCasualty;
|
|
protected BaseCompartmentSlot m_ERRFixtureCasualtySeat;
|
|
protected Vehicle m_ERRFixtureFirst, m_ERRFixtureSecond;
|
|
protected bool m_ERRFixturePadsReady;
|
|
protected ref ERR_ReconAirports m_ERRFixtureAirports;
|
|
protected SCR_CompartmentAccessComponent m_ERRFixtureCasualtyAccess;
|
|
protected override void EOnInit(IEntity owner)
|
|
{
|
|
super.EOnInit(owner);
|
|
string enabled, outcome;
|
|
if (SCR_Global.IsEditMode() || !Replication.IsServer() || !System.GetCLIParam("errBaseSmoke", enabled) || enabled != "1"
|
|
|| !System.GetCLIParam("errSmokeOutcome", outcome) || outcome != "rescue-faction") return;
|
|
System.GetCLIParam("errRescueFixtureScope", m_ERRFixtureScope);
|
|
System.GetCLIParam("errRescueFixtureFaction", m_ERRFixtureFactionId);
|
|
m_ERRFixtureFactionKey = "US"; if (m_ERRFixtureFactionId == "fia") m_ERRFixtureFactionKey = "FIA";
|
|
m_ERRFixtureTransportCount = ERR_RescueAssets.TransportCount(m_ERRFixtureFactionKey);
|
|
m_ERRFixtureDeadline = System.GetTickCount() + 540000;
|
|
GetGame().GetCallqueue().CallLater(ERR_RescueFactionTick, 250, true);
|
|
}
|
|
protected bool ERR_RescueFactionCheck(bool value, string detail)
|
|
{
|
|
if (value) { Print("ERR SMOKE PASS: rescue-faction " + detail); return true; }
|
|
Print("ERR SMOKE FAIL: rescue-faction " + detail, LogLevel.ERROR);
|
|
GetGame().GetCallqueue().Remove(ERR_RescueFactionTick); return false;
|
|
}
|
|
protected void ERR_RescueFactionMove(vector position)
|
|
{
|
|
vector transform[4]; m_ERRFixturePlayer.GetWorldTransform(transform); transform[3] = ERR_Actors.Ground(position);
|
|
ChimeraCharacter.Cast(m_ERRFixturePlayer).Teleport(transform);
|
|
}
|
|
protected void ERR_RescueFactionTrace(float now)
|
|
{
|
|
if (now < m_ERRFixtureNextLog) return;
|
|
m_ERRFixtureNextLog = now + 10000;
|
|
foreach (IEntity entity : m_OwnedEntities)
|
|
{
|
|
if (!entity) continue;
|
|
ERR_RescueFlightComponent flight = ERR_RescueFlightComponent.Cast(entity.FindComponent(ERR_RescueFlightComponent));
|
|
if (flight) PrintFormat("ERR RESCUE FACTION TRACE step=%1 stage=%2 prefab=%3 pos=%4 status=%5 cargo=%6", m_ERRFixtureStep, PickupStage(), entity.GetPrefabData().GetPrefabName(), entity.GetOrigin(), flight.Status(), flight.ERR_FixtureCargo());
|
|
}
|
|
}
|
|
protected void ERR_RescueFactionTick()
|
|
{
|
|
if (System.GetTickCount() > m_ERRFixtureDeadline) { ERR_RescueFactionCheck(false, "overall fixture deadline"); return; }
|
|
if (!PreparationError().IsEmpty() || Phase() == ERR_Phase.DEFEAT) { ERR_RescueFactionCheck(false, "mission failed: " + PreparationError()); return; }
|
|
if (!Active()) return;
|
|
float now = GetWorld().GetWorldTime();
|
|
if (!m_ERRFixturePlayer)
|
|
{
|
|
PlayerController controller = GetGame().GetPlayerController();
|
|
if (!controller || !controller.GetControlledEntity() || !Extraction().PickupConfigured() || Phase() != ERR_Phase.PRISON) return;
|
|
m_ERRFixturePlayer = controller.GetControlledEntity();
|
|
DamageManagerComponent.Cast(m_ERRFixturePlayer.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
|
if (!ERR_RescueFactionCheck(PlayerFaction().GetFactionKey() == m_ERRFixtureFactionKey, "normal profile selected rescue faction " + m_ERRFixtureFactionKey)) return;
|
|
if (m_ERRFixtureScope == "preparation")
|
|
{
|
|
if (!ERR_RescueFactionCheck(Extraction().ERR_FixturePads(), "current production preparation retains two surveyed landing pads")) return;
|
|
Print("ERR SMOKE COMPLETE: rescue-faction final source compilation and production dual-pad preparation at arranged flat input");
|
|
GetGame().GetCallqueue().Remove(ERR_RescueFactionTick); return;
|
|
}
|
|
}
|
|
foreach (IEntity entity : m_OwnedEntities)
|
|
{
|
|
if (!entity) continue;
|
|
ERR_RescueFlightComponent flight = ERR_RescueFlightComponent.Cast(entity.FindComponent(ERR_RescueFlightComponent));
|
|
if (flight) flight.ERR_FixtureProtect();
|
|
}
|
|
if (now < m_ERRFixtureWait) return;
|
|
if (m_ERRFixtureStep == 0)
|
|
{
|
|
|
|
m_ERRFixtureStep = 1;
|
|
}
|
|
ERR_RescueFactionTrace(now);
|
|
if (m_ERRFixtureStep == 1)
|
|
{
|
|
if (!m_ERRFixturePadsReady)
|
|
{
|
|
if (!m_ERRFixtureAirports && Extraction().ERR_FixturePads()) m_ERRFixturePadsReady = true;
|
|
else
|
|
{
|
|
if (!m_ERRFixtureAirports)
|
|
{
|
|
PrintFormat("ERR RESCUE FACTION ARRANGED: original pickup %1 lacks a second surveyed pad; flight fixture will use surveyed airport grounds", Extraction().PickupPosition());
|
|
m_ERRFixtureAirports = new ERR_ReconAirports();
|
|
}
|
|
for (int tile = 0; tile < 8; tile++) if (!m_ERRFixtureAirports.Complete()) m_ERRFixtureAirports.Update();
|
|
if (!m_ERRFixtureAirports.Complete()) return;
|
|
foreach (vector centre : m_ERRFixtureAirports.m_Positions)
|
|
{
|
|
for (int band = 0; band < 4 && !m_ERRFixturePadsReady; band++)
|
|
for (int bearing = 0; bearing < 16 && !m_ERRFixturePadsReady; bearing++)
|
|
{
|
|
float angle = bearing * Math.PI2 / 16;
|
|
vector candidate = ERR_Actors.Ground(centre + Vector(Math.Cos(angle), 0, Math.Sin(angle)) * (band * 80));
|
|
if (Extraction().ERR_FixtureAirportPickup(candidate)) m_ERRFixturePadsReady = true;
|
|
}
|
|
if (m_ERRFixturePadsReady) break;
|
|
}
|
|
if (!ERR_RescueFactionCheck(m_ERRFixturePadsReady, "arranged airport pickup has two production-surveyed landing footprints")) return;
|
|
}
|
|
}
|
|
if (!ERR_RescueFactionCheck(Transition(ERR_Phase.RADIO_SEARCH) && Transition(ERR_Phase.RADIO_DEFENCE) && Transition(ERR_Phase.EXTRACTION), "arranged valid phase path enables native rescue flight")) return;
|
|
Extraction().OpenPickup(); Extraction().ERR_FixtureDispatch();
|
|
m_ERRFixtureStep = 10; return;
|
|
}
|
|
if (m_ERRFixtureStep == 10)
|
|
{
|
|
if (PickupStage() != 3) return;
|
|
array<Vehicle> vehicles = {}; Extraction().Vehicles(vehicles);
|
|
if (!ERR_RescueFactionCheck(vehicles.Count() == m_ERRFixtureTransportCount, "production dispatch registers expected transport count " + m_ERRFixtureTransportCount.ToString())) return;
|
|
m_ERRFixtureFirst = vehicles[0]; m_ERRFixtureSecond = vehicles[vehicles.Count() - 1];
|
|
m_ERRFixtureStep = 2; return;
|
|
}
|
|
if (m_ERRFixtureStep == 2)
|
|
{
|
|
if (PickupStage() != 4) return;
|
|
int coverCount;
|
|
foreach (IEntity coverEntity : m_OwnedEntities)
|
|
{
|
|
if (!coverEntity || !Vehicle.Cast(coverEntity) || Extraction().IsTransport(Vehicle.Cast(coverEntity))) continue;
|
|
ERR_RescueFlightComponent cover = ERR_RescueFlightComponent.Cast(coverEntity.FindComponent(ERR_RescueFlightComponent));
|
|
if (cover && !cover.Lost() && cover.Phase() == ERR_ReconPhase.SEARCH && SCR_FactionManager.SGetFaction(coverEntity) == PlayerFaction()) coverCount++;
|
|
}
|
|
if (!ERR_RescueFactionCheck(coverCount == 2, "both friendly armed escorts remain airborne during pickup")) return;
|
|
if (!ERR_RescueFactionCheck(ERR_CasualtyLoadingRules.AircraftReady(m_ERRFixtureFirst) && ERR_CasualtyLoadingRules.AircraftReady(m_ERRFixtureSecond), "all registered transports physically landed upright and stationary")) return;
|
|
if (m_ERRFixtureTransportCount > 1 && !ERR_RescueFactionCheck(vector.DistanceXZ(m_ERRFixtureFirst.GetOrigin(), m_ERRFixtureSecond.GetOrigin()) >= 30, "actual transport landing positions keep rotor discs separated")) return;
|
|
vector patientPosition = ERR_Actors.Ground(m_ERRFixtureSecond.GetOrigin() + "6 0 0");
|
|
m_ERRFixtureCasualty = ERR_Actors.Spawn(Factions().PrisonerPrefab, patientPosition); Own(m_ERRFixtureCasualty);
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureCasualty != null, "independent living fixture survivor created beside last registered transport")) return;
|
|
m_ERRFixtureCasualtyAccess = SCR_CompartmentAccessComponent.Cast(m_ERRFixtureCasualty.FindComponent(SCR_CompartmentAccessComponent));
|
|
SCR_CharacterDamageManagerComponent damage = SCR_CharacterDamageManagerComponent.Cast(m_ERRFixtureCasualty.FindComponent(SCR_CharacterDamageManagerComponent));
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureCasualtyAccess && damage, "native casualty components present")) return;
|
|
damage.ForceUnconsciousness(); damage.EnableDamageHandling(false);
|
|
ERR_RescueFactionMove(patientPosition + "2 0 0");
|
|
m_ERRFixtureWait = now + 6000; m_ERRFixtureStep = 3; return;
|
|
}
|
|
if (m_ERRFixtureStep == 3)
|
|
{
|
|
if (!ERR_RescueFactionCheck(ERR_Actors.Alive(m_ERRFixtureCasualty) && !ERR_Actors.CanFight(m_ERRFixtureCasualty) && !CompartmentAccessComponent.GetVehicleIn(m_ERRFixtureCasualty), "patient is already unconscious on ground before cargo request")) return;
|
|
if (!ERR_RescueFactionCheck(CasualtyLoadingAircraftFor(m_ERRFixturePlayer, m_ERRFixtureCasualty) == m_ERRFixtureSecond, "production casualty selection chooses the nearby last registered transport")) return;
|
|
ERR_RescueFlightComponent second = ERR_RescueFlightComponent.Cast(m_ERRFixtureSecond.FindComponent(ERR_RescueFlightComponent));
|
|
BaseCompartmentManagerComponent manager = BaseCompartmentManagerComponent.Cast(m_ERRFixtureSecond.FindComponent(BaseCompartmentManagerComponent));
|
|
array<BaseCompartmentSlot> rootSeats = {}; if (manager) manager.GetCompartments(rootSeats);
|
|
int rootCargo, rootBenches;
|
|
foreach (BaseCompartmentSlot rootSeat : rootSeats)
|
|
{
|
|
if (rootSeat && rootSeat.GetType() == ECompartmentType.CARGO && rootSeat.IsCompartmentAccessible() && !rootSeat.IsOccupied() && !rootSeat.IsReserved() && !rootSeat.IsGetInLockedFor(m_ERRFixtureCasualty)) rootCargo++;
|
|
if (ERR_CasualtyLoadingRules.TransportBench(rootSeat, m_ERRFixtureSecond)) rootBenches++;
|
|
}
|
|
PrintFormat("ERR RESCUE FACTION ROOT SEATS total=%1 freeCargo=%2 auditedBenches=%3 recursiveFreeCargo=%4", rootSeats.Count(), rootCargo, rootBenches, second.ERR_FixtureCargo());
|
|
if (!ERR_RescueFactionCheck(rootCargo >= ERR_RescueAssets.TransportSeats(m_ERRFixtureFactionKey) && rootBenches == rootCargo, "native root manager enumerates all audited accessible transport cargo benches")) return;
|
|
if (!ERR_RescueFactionCheck(ERR_CasualtyFeedback.PhysicalReason(this, m_ERRFixturePlayer, m_ERRFixtureCasualty).IsEmpty(), "production physical feedback accepts native ground casualty beside last registered transport")) return;
|
|
m_ERRFixtureCasualtySeat = CasualtyLoading().ERR_FixtureAvailableSeat(m_ERRFixtureCasualty, m_ERRFixtureSecond);
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureCasualtySeat && ERR_CasualtyLoadingRules.TransportBench(m_ERRFixtureCasualtySeat, m_ERRFixtureSecond), "production casualty service selects an audited seat in last registered transport")) return;
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureCasualtySeat != null && m_ERRFixtureCasualtyAccess.MoveInVehicle(m_ERRFixtureSecond, ECompartmentType.CARGO, false, m_ERRFixtureCasualtySeat), "native unconscious cargo loading accepted by last registered transport")) return;
|
|
m_ERRFixtureWait = now + 2000; m_ERRFixtureStep = 4; return;
|
|
}
|
|
if (m_ERRFixtureStep == 4)
|
|
{
|
|
if (!ERR_RescueFactionCheck(CompartmentAccessComponent.GetVehicleIn(m_ERRFixtureCasualty) == m_ERRFixtureSecond && m_ERRFixtureCasualtySeat.GetOccupant() == m_ERRFixtureCasualty && ERR_Actors.Alive(m_ERRFixtureCasualty) && !ERR_Actors.CanFight(m_ERRFixtureCasualty), "living unconscious casualty physically occupies exact cargo seat in last registered transport")) return;
|
|
if (!ERR_RescueFactionCheck(m_Roster.Bind(10210, "00e77200-0000-0000-0000-000000000210"), "isolated casualty fixture identity registered for fleet departure")) return;
|
|
m_Roster.Spawned(10210, m_ERRFixtureCasualty);
|
|
ERR_RescueFactionMove(m_ERRFixtureFirst.GetOrigin() + "6 0 0");
|
|
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(m_ERRFixturePlayer.FindComponent(CompartmentAccessComponent));
|
|
ERR_RescueFlightComponent first = ERR_RescueFlightComponent.Cast(m_ERRFixtureFirst.FindComponent(ERR_RescueFlightComponent));
|
|
BaseCompartmentSlot seat = first.ERR_FixturePassengerSeat();
|
|
if (!ERR_RescueFactionCheck(access && seat && access.GetInVehicle(seat.GetOwner(), seat, true, -1, ECloseDoorAfterActions.INVALID, false), "original controlled player boards first transport through native cargo access")) return;
|
|
m_ERRFixtureWait = now + 15000; m_ERRFixtureStep = 5; return;
|
|
}
|
|
if (m_ERRFixtureStep == 5)
|
|
{
|
|
if (!ERR_RescueFactionCheck(CompartmentAccessComponent.GetVehicleIn(m_ERRFixturePlayer) == m_ERRFixtureFirst && CompartmentAccessComponent.GetVehicleIn(m_ERRFixtureCasualty) == m_ERRFixtureSecond, "original player and living wounded survivor remain physically retained in their assigned transports")) return;
|
|
if (!ERR_RescueFactionCheck(PickupStage() == 5 && Extraction().CarryingSurvivorsToSafety(), "production eight-second boarding hold accepts survivors aboard the registered fleet")) return;
|
|
m_ERRFixtureDepartureDeadline = now + 45000; m_ERRFixtureStep = 6;
|
|
}
|
|
if (m_ERRFixtureStep == 6)
|
|
{
|
|
bool bothAirborne = true;
|
|
array<Vehicle> departing = {}; Extraction().Vehicles(departing);
|
|
foreach (int index, Vehicle aircraft : departing)
|
|
{
|
|
Physics physics = aircraft.GetPhysics();
|
|
HelicopterControllerComponent controller = HelicopterControllerComponent.Cast(aircraft.FindComponent(HelicopterControllerComponent));
|
|
VehicleHelicopterSimulation simulation; if (controller) simulation = controller.GetSimulation();
|
|
ERR_RescueFlightComponent flight = ERR_RescueFlightComponent.Cast(aircraft.FindComponent(ERR_RescueFlightComponent));
|
|
if (!flight || flight.Lost()) { ERR_RescueFactionCheck(false, "departure must retain original living transport " + index.ToString()); return; }
|
|
float altitude = aircraft.GetOrigin()[1] - SCR_TerrainHelper.GetTerrainY(aircraft.GetOrigin(), GetWorld(), noUnderwater: true);
|
|
vector velocity; if (physics) velocity = physics.GetVelocity();
|
|
bool climbing = simulation && simulation.EngineIsOn() && !simulation.HasAnyGroundContact() && altitude > 10 && velocity[1] > 0.5;
|
|
if (!climbing) bothAirborne = false;
|
|
if (now >= m_ERRFixtureDepartureLog)
|
|
PrintFormat("ERR RESCUE FACTION DEPARTURE aircraft=%1 engineOn=%2 contact=%3 physicsVelocity=%4 agl=%5 phase=%6 climbing=%7 physicsActive=%8", index, simulation && simulation.EngineIsOn(), simulation && simulation.HasAnyGroundContact(), velocity, altitude, flight.Status(), climbing, physics && physics.IsActive());
|
|
}
|
|
if (now >= m_ERRFixtureDepartureLog) m_ERRFixtureDepartureLog = now + 1000;
|
|
if (!bothAirborne)
|
|
{
|
|
if (now >= m_ERRFixtureDepartureDeadline) ERR_RescueFactionCheck(false, "all loaded transports must climb above ten metres without native ground contact before departure deadline");
|
|
return;
|
|
}
|
|
if (!ERR_RescueFactionCheck(CompartmentAccessComponent.GetVehicleIn(m_ERRFixturePlayer) == m_ERRFixtureFirst && CompartmentAccessComponent.GetVehicleIn(m_ERRFixtureCasualty) == m_ERRFixtureSecond && ERR_Actors.Alive(m_ERRFixtureCasualty), "all transports genuinely airborne and climbing above ten metres with original survivors retained")) return;
|
|
Print("ERR SMOKE COMPLETE: rescue faction scope=" + m_ERRFixtureScope + " faction=" + m_ERRFixtureFactionKey + " measured landing, wounded boarding and verified airborne departure; arranged local fixture, not dedicated multiplayer or boundary qualification");
|
|
GetGame().GetCallqueue().Remove(ERR_RescueFactionTick);
|
|
}
|
|
}
|
|
}
|
|
#endif
|