Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
356 lines
23 KiB
C
356 lines
23 KiB
C
#ifdef WORKBENCH
|
|
// Arranged local native fixture: freezes matrix-only craft, protects the test
|
|
// aircraft and crew, and directly dispatches the real production rescue service.
|
|
// No simulation/compartment override is used by the landing/boarding flow.
|
|
modded class ERR_RescueFlightComponent
|
|
{
|
|
Faction ERR_FixtureFaction;
|
|
bool ERR_FixtureHold;
|
|
protected override Faction CrewFaction()
|
|
{
|
|
if (ERR_FixtureFaction) return ERR_FixtureFaction;
|
|
return super.CrewFaction();
|
|
}
|
|
int ERR_FixtureCargo() { return CargoCapacity(m_Aircraft); }
|
|
int ERR_FixtureCrew() { return m_OriginalCrew.Count(); }
|
|
bool ERR_FixtureIdentity(ResourceName expected)
|
|
{
|
|
if (!m_Aircraft || !m_Crew || m_Crew.GetFaction() != CrewFaction() || SCR_FactionManager.SGetFaction(m_Aircraft) != CrewFaction() || m_Seats.Count() < 2 || m_OriginalCrew.Count() != m_Seats.Count()) return false;
|
|
foreach (BaseCompartmentSlot seat : m_Seats)
|
|
{
|
|
IEntity actor = seat.GetOccupant();
|
|
if (!actor || !m_OriginalCrew.Contains(actor) || !ERR_Actors.CanFight(actor) || SCR_FactionManager.SGetFaction(actor) != CrewFaction()) return false;
|
|
if (!actor.GetPrefabData() || actor.GetPrefabData().GetPrefabName() != expected) return false;
|
|
CompartmentAccessComponent access = CompartmentAccessComponent.Cast(actor.FindComponent(CompartmentAccessComponent));
|
|
if (!access || access.GetCompartment() != seat || CompartmentAccessComponent.GetVehicleIn(actor) != m_Aircraft) return false;
|
|
}
|
|
return true;
|
|
}
|
|
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];
|
|
}
|
|
void ERR_FixtureDelete()
|
|
{
|
|
Release(false);
|
|
foreach (IEntity actor : m_OriginalCrew) if (actor) SCR_EntityHelper.DeleteEntityAndChildren(actor);
|
|
m_OriginalCrew.Clear(); m_Seats.Clear();
|
|
if (m_Crew) SCR_EntityHelper.DeleteEntityAndChildren(m_Crew);
|
|
m_Crew = null;
|
|
if (m_Aircraft) SCR_EntityHelper.DeleteEntityAndChildren(m_Aircraft);
|
|
m_Aircraft = null;
|
|
}
|
|
override void EOnSimulate(IEntity owner, float timeSlice)
|
|
{
|
|
if (ERR_FixtureHold)
|
|
{
|
|
Physics physics = owner.GetPhysics();
|
|
if (physics) { physics.SetVelocity(vector.Zero); physics.SetAngularVelocity(vector.Zero); }
|
|
return;
|
|
}
|
|
super.EOnSimulate(owner, timeSlice);
|
|
}
|
|
}
|
|
modded class ERR_CasualtyLoadingService
|
|
{
|
|
BaseCompartmentSlot ERR_FixtureAvailableSeat(IEntity patient, Vehicle aircraft) { return AvailableSeat(patient, aircraft); }
|
|
}
|
|
modded class ERR_FactionSelection
|
|
{
|
|
void ERR_FixturePlayerChoice(string id) { m_Resolved.Set("escapists", id); }
|
|
}
|
|
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 ref array<string> m_ERRFixtureKeys = {"US", "USSR", "FIA", "RHS_USAF", "RHS_AFRF", "RHS_ION"};
|
|
protected int m_ERRFixtureIndex, m_ERRFixtureStep;
|
|
protected string m_ERRFixtureScope;
|
|
protected float m_ERRFixtureDeadline, m_ERRFixtureWait, m_ERRFixtureNextLog, m_ERRFixtureMatrixReady;
|
|
protected ResourceName m_ERRFixtureExpectedCrew;
|
|
protected float m_ERRFixtureDepartureDeadline, m_ERRFixtureDepartureLog;
|
|
protected ERR_RescueFlightComponent m_ERRFixtureMatrix;
|
|
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);
|
|
if (m_ERRFixtureScope == "flight" || m_ERRFixtureScope == "flight-short" || m_ERRFixtureScope == "preparation") m_ERRFixtureIndex = m_ERRFixtureKeys.Count() * 2;
|
|
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() == "US", "normal profile selected US rescue faction")) 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)
|
|
{
|
|
if (!ERR_RescueFactionMatrix(now)) return;
|
|
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() == 2, "production US dispatch registers exactly two transports")) return;
|
|
m_ERRFixtureFirst = vehicles[0]; m_ERRFixtureSecond = vehicles[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 two-ship pickup")) return;
|
|
if (!ERR_RescueFactionCheck(ERR_CasualtyLoadingRules.AircraftReady(m_ERRFixtureFirst) && ERR_CasualtyLoadingRules.AircraftReady(m_ERRFixtureSecond), "both US transports physically landed upright and stationary")) return;
|
|
if (!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 second 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 second 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 == 8 && rootBenches == 8, "native root manager enumerates all eight audited accessible Huey cargo benches")) return;
|
|
if (!ERR_RescueFactionCheck(ERR_CasualtyFeedback.PhysicalReason(this, m_ERRFixturePlayer, m_ERRFixtureCasualty).IsEmpty(), "production physical feedback accepts native ground casualty beside second 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 second transport")) return;
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureCasualtySeat != null && m_ERRFixtureCasualtyAccess.MoveInVehicle(m_ERRFixtureSecond, ECompartmentType.CARGO, false, m_ERRFixtureCasualtySeat), "native unconscious cargo loading accepted by second 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 second 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 distributed across two transports")) return;
|
|
if (!ERR_RescueFactionCheck(PickupStage() == 5 && Extraction().CarryingSurvivorsToSafety(), "production eight-second boarding hold accepts survivors distributed across the fleet")) return;
|
|
m_ERRFixtureDepartureDeadline = now + 45000; m_ERRFixtureStep = 6;
|
|
}
|
|
if (m_ERRFixtureStep == 6)
|
|
{
|
|
bool bothAirborne = true;
|
|
array<Vehicle> departing = {m_ERRFixtureFirst, m_ERRFixtureSecond};
|
|
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, "both 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), "both transports genuinely airborne and climbing above ten metres with original distributed survivors retained")) return;
|
|
Print("ERR SMOKE COMPLETE: rescue faction scope=" + m_ERRFixtureScope + " protected US two-transport landing, wounded boarding and verified airborne departure; arranged local fixture, not dedicated multiplayer or boundary qualification");
|
|
GetGame().GetCallqueue().Remove(ERR_RescueFactionTick);
|
|
}
|
|
}
|
|
protected bool ERR_RescueFactionMatrix(float now)
|
|
{
|
|
if (m_ERRFixtureIndex >= m_ERRFixtureKeys.Count() * 2)
|
|
{
|
|
m_SelectedPlayerFaction = "US"; FactionSelection().ERR_FixturePlayerChoice("us"); return true;
|
|
}
|
|
string key = m_ERRFixtureKeys[m_ERRFixtureIndex / 2];
|
|
bool escort = m_ERRFixtureIndex % 2 == 1;
|
|
if (!m_ERRFixtureMatrix)
|
|
{
|
|
Faction faction = GetGame().GetFactionManager().GetFactionByKey(key);
|
|
ERR_FactionChoice choice;
|
|
foreach (ERR_FactionChoice candidate : ERR_FactionChoices.All()) if (candidate.Key == key) choice = candidate;
|
|
if (!ERR_RescueFactionCheck(faction && choice, "registered native faction and selected content choice " + key)) return false;
|
|
m_SelectedPlayerFaction = key; FactionSelection().ERR_FixturePlayerChoice(choice.Id);
|
|
if (!ERR_RescueFactionCheck(PlayerFaction() == faction, "arranged matrix selection uses actual native faction " + key)) return false;
|
|
m_ERRFixtureExpectedCrew = ERR_RescueAssets.Pilot(key);
|
|
if (m_ERRFixtureExpectedCrew.IsEmpty())
|
|
{
|
|
Resource group = Resource.Load(choice.Group);
|
|
IEntitySource source; if (group && group.IsValid()) source = group.GetResource().ToEntitySource();
|
|
array<ResourceName> members = {};
|
|
if (!ERR_RescueFactionCheck(source && source.Get("m_aUnitPrefabSlots", members) && !members.IsEmpty(), "selected non-pilot faction group has native crew member " + key)) return false;
|
|
m_ERRFixtureExpectedCrew = members[0];
|
|
}
|
|
if (!ERR_RescueFactionCheck(ERR_RescueAssets.Crew(this) == m_ERRFixtureExpectedCrew, "mode crew selection matches independently selected faction resource " + key)) return false;
|
|
if (!ERR_RescueFactionCheck(!m_ERRFixtureExpectedCrew.IsEmpty(), "production matching crew resource resolves " + key)) return false;
|
|
ResourceName prefab = ERR_RescueAssets.Aircraft(key, escort);
|
|
vector position = Extraction().PickupPosition() + "500 220 500";
|
|
Vehicle aircraft = Vehicle.Cast(ERR_Actors.Spawn(prefab, position)); Own(aircraft);
|
|
if (!ERR_RescueFactionCheck(aircraft != null, "production aircraft resource loads " + key + " escort=" + escort.ToString())) return false;
|
|
m_ERRFixtureMatrix = ERR_RescueFlightComponent.Cast(aircraft.FindComponent(ERR_RescueFlightComponent));
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureMatrix != null, "production native flight component present " + key)) return false;
|
|
m_ERRFixtureMatrix.ERR_FixtureFaction = faction; m_ERRFixtureMatrix.ERR_FixtureHold = true;
|
|
m_ERRFixtureMatrix.StartRescue(this, position, Extraction().PickupPosition(), 10 + m_ERRFixtureIndex, escort);
|
|
m_ERRFixtureMatrixReady = 0; return false;
|
|
}
|
|
m_ERRFixtureMatrix.Update(now); m_ERRFixtureMatrix.ERR_FixtureProtect();
|
|
if (m_ERRFixtureMatrix.Lost()) { ERR_RescueFactionCheck(false, "native matrix preparation " + key + ": " + m_ERRFixtureMatrix.Status()); return false; }
|
|
if (m_ERRFixtureMatrix.Phase() == ERR_ReconPhase.CREW) return false;
|
|
if (m_ERRFixtureMatrixReady == 0) { m_ERRFixtureMatrixReady = now + 1000; return false; }
|
|
if (now < m_ERRFixtureMatrixReady) return false;
|
|
if (!ERR_RescueFactionCheck(m_ERRFixtureMatrix.ERR_FixtureIdentity(m_ERRFixtureExpectedCrew), "native original living crew occupy exact pilot/gunner seats with matching prefab and faction " + key + " escort=" + escort.ToString())) return false;
|
|
int capacity = m_ERRFixtureMatrix.ERR_FixtureCargo();
|
|
PrintFormat("ERR RESCUE FACTION MATRIX key=%1 escort=%2 aircraft=%3 crew=%4 crewCount=%5 freeCargo=%6 transports=%7", key, escort, ERR_RescueAssets.Aircraft(key, escort), m_ERRFixtureExpectedCrew, m_ERRFixtureMatrix.ERR_FixtureCrew(), capacity, ERR_RescueAssets.TransportCount(key));
|
|
if (!escort && !ERR_RescueFactionCheck(capacity >= ERR_RescueAssets.TransportSeats(key) && capacity * ERR_RescueAssets.TransportCount(key) >= ERR_ExtractionService.REQUIRED_SEATS, "native transport capacity preserves full twelve-survivor roster " + key)) return false;
|
|
m_ERRFixtureMatrix.ERR_FixtureDelete(); m_ERRFixtureMatrix = null; m_ERRFixtureIndex++; return false;
|
|
}
|
|
}
|
|
#endif
|