Build independent Everon successor foundation and native regressions
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
// These samples exist only at intake. HQ never revisits their entity loadouts.
|
||||
class ERR_HQVisualContact
|
||||
{
|
||||
IEntity Entity;
|
||||
vector Position;
|
||||
float ObservedAt;
|
||||
bool Escapist;
|
||||
string Travel = "unknown";
|
||||
string Weapon = "unknown";
|
||||
}
|
||||
|
||||
class ERR_HQObservation
|
||||
{
|
||||
// Require current native line of sight before reading visible equipment.
|
||||
static bool IsClearSighting(float age, float traceFraction, float exposure)
|
||||
{
|
||||
return age >= 0 && age <= 0.5 && traceFraction >= 1 && exposure > 0;
|
||||
}
|
||||
|
||||
static bool CanIdentifyWeapon(float distance, float exposure)
|
||||
{
|
||||
return distance >= 0 && distance <= 150 && exposure >= 0.5;
|
||||
}
|
||||
|
||||
static string WeaponCategory(EWeaponType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case EWeaponType.WT_HANDGUN: return "pistol";
|
||||
case EWeaponType.WT_RIFLE: return "rifle";
|
||||
case EWeaponType.WT_SNIPERRIFLE: return "sniper rifle";
|
||||
case EWeaponType.WT_MACHINEGUN: return "machine gun";
|
||||
case EWeaponType.WT_ROCKETLAUNCHER: return "rocket launcher";
|
||||
case EWeaponType.WT_GRENADELAUNCHER: return "grenade launcher";
|
||||
case EWeaponType.WT_FRAGGRENADE: return "grenade";
|
||||
case EWeaponType.WT_SMOKEGRENADE: return "smoke grenade";
|
||||
}
|
||||
return "unidentified weapon";
|
||||
}
|
||||
|
||||
static ERR_HQVisualContact Capture(BaseTarget target, float now)
|
||||
{
|
||||
if (!target || target.GetTargetCategory() != ETargetCategory.ENEMY || !IsClearSighting(target.GetTimeSinceSeen(), target.GetTraceFraction(), target.GetExposure()))
|
||||
return null;
|
||||
DamageManagerComponent damage = target.GetDamageManagerComponent();
|
||||
if (damage && damage.IsDestroyed())
|
||||
return null;
|
||||
IEntity entity = target.GetTargetEntity();
|
||||
if (!entity)
|
||||
return null;
|
||||
ERR_HQVisualContact seen = new ERR_HQVisualContact();
|
||||
seen.Entity = entity;
|
||||
seen.Position = target.GetLastSeenPosition();
|
||||
seen.ObservedAt = now - target.GetTimeSinceSeen() * 1000;
|
||||
SCR_ChimeraCharacter character = SCR_ChimeraCharacter.Cast(entity);
|
||||
if (character)
|
||||
{
|
||||
if (GetGame().GetPlayerManager())
|
||||
seen.Escapist = GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(entity) > 0;
|
||||
seen.Travel = "on foot";
|
||||
if (CompartmentAccessComponent.GetVehicleIn(entity))
|
||||
seen.Travel = "in vehicle";
|
||||
// Read only the drawn weapon; never enumerate weapons or inventory.
|
||||
if (CanIdentifyWeapon(target.GetDistance(), target.GetExposure()))
|
||||
{
|
||||
CharacterControllerComponent controller = CharacterControllerComponent.Cast(entity.FindComponent(CharacterControllerComponent));
|
||||
if (controller)
|
||||
{
|
||||
IEntity heldItem = controller.GetCurrentItemInHands();
|
||||
BaseWeaponComponent weapon;
|
||||
if (heldItem)
|
||||
weapon = BaseWeaponComponent.Cast(heldItem.FindComponent(BaseWeaponComponent));
|
||||
seen.Weapon = "none drawn";
|
||||
if (weapon)
|
||||
seen.Weapon = WeaponCategory(weapon.GetWeaponType());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Vehicle.Cast(entity))
|
||||
{
|
||||
seen.Travel = "vehicle";
|
||||
if (target.GetUnitType() == EAIUnitType.UnitType_Aircraft)
|
||||
seen.Travel = "aircraft";
|
||||
}
|
||||
return seen;
|
||||
}
|
||||
|
||||
static void Gather(SCR_AIGroupUtilityComponent utility, float now, out array<ref ERR_HQVisualContact> samples)
|
||||
{
|
||||
samples.Clear();
|
||||
if (!utility)
|
||||
return;
|
||||
array<BaseTarget> visible = {};
|
||||
utility.ERR_HQGetVisualTargets(visible);
|
||||
foreach (BaseTarget target : visible)
|
||||
{
|
||||
ERR_HQVisualContact seen = Capture(target, now);
|
||||
if (!seen)
|
||||
continue;
|
||||
ERR_HQVisualContact existing;
|
||||
foreach (ERR_HQVisualContact sample : samples)
|
||||
{
|
||||
if (sample.Entity == seen.Entity)
|
||||
{
|
||||
existing = sample;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!existing)
|
||||
{
|
||||
if (samples.Count() < 64)
|
||||
samples.Insert(seen);
|
||||
}
|
||||
else if (seen.ObservedAt > existing.ObservedAt || (seen.ObservedAt == existing.ObservedAt && existing.Weapon == "unknown" && seen.Weapon != "unknown"))
|
||||
{
|
||||
existing.Position = seen.Position;
|
||||
existing.ObservedAt = seen.ObservedAt;
|
||||
existing.Travel = seen.Travel;
|
||||
existing.Weapon = seen.Weapon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Describe(ERR_HQReport report, array<ref ERR_HQVisualContact> samples, float now)
|
||||
{
|
||||
// New observations must not silently inherit old equipment/travel details.
|
||||
report.SeenEscapists = -1;
|
||||
report.TravelMode = "unknown";
|
||||
report.SeenWeapons = "unknown";
|
||||
report.DetailsObservedAt = -1;
|
||||
if (report.Distress)
|
||||
return;
|
||||
ERR_HQVisualContact anchor;
|
||||
foreach (ERR_HQVisualContact sample : samples)
|
||||
{
|
||||
if (sample.Entity == report.Target && now - sample.ObservedAt >= 0 && now - sample.ObservedAt <= 500)
|
||||
{
|
||||
anchor = sample;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anchor)
|
||||
return;
|
||||
report.DetailsObservedAt = anchor.ObservedAt;
|
||||
report.TravelMode = anchor.Travel;
|
||||
report.SeenWeapons = anchor.Weapon;
|
||||
array<IEntity> counted = {};
|
||||
array<string> weapons = {};
|
||||
string travel;
|
||||
foreach (ERR_HQVisualContact sighting : samples)
|
||||
{
|
||||
if (!sighting.Escapist || !sighting.Entity || counted.Contains(sighting.Entity) || now - sighting.ObservedAt < 0 || now - sighting.ObservedAt > 500 || vector.DistanceXZ(anchor.Position, sighting.Position) > 100)
|
||||
continue;
|
||||
counted.Insert(sighting.Entity);
|
||||
if (travel.IsEmpty())
|
||||
travel = sighting.Travel;
|
||||
else if (travel != sighting.Travel)
|
||||
travel = "mixed travel";
|
||||
if (sighting.Weapon != "unknown" && !weapons.Contains(sighting.Weapon) && weapons.Count() < 4)
|
||||
weapons.Insert(sighting.Weapon);
|
||||
}
|
||||
if (counted.IsEmpty())
|
||||
return; // A visible vehicle does not reveal its hidden occupants.
|
||||
report.SeenEscapists = counted.Count();
|
||||
// A vehicle anchor remains a vehicle report, even with people near it.
|
||||
if (anchor.Escapist)
|
||||
report.TravelMode = travel;
|
||||
report.SeenWeapons = "unknown";
|
||||
if (!weapons.IsEmpty())
|
||||
{
|
||||
report.SeenWeapons = weapons[0];
|
||||
for (int i = 1; i < weapons.Count(); i++)
|
||||
report.SeenWeapons += ", " + weapons[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modded class SCR_AIGroupUtilityComponent
|
||||
{
|
||||
void ERR_HQGetVisualTargets(out array<BaseTarget> targets)
|
||||
{
|
||||
targets.Clear();
|
||||
foreach (SCR_AIInfoComponent info : m_aInfoComponents)
|
||||
{
|
||||
if (!info || !info.m_Perception)
|
||||
continue;
|
||||
AIAgent observer = AIAgent.Cast(info.GetOwner());
|
||||
if (!observer || !ERR_Actors.CanFight(observer.GetControlledEntity()))
|
||||
continue;
|
||||
array<BaseTarget> own = {};
|
||||
info.m_Perception.GetTargetsList(own, ETargetCategory.ENEMY);
|
||||
foreach (BaseTarget target : own)
|
||||
{
|
||||
if (target && ERR_HQObservation.IsClearSighting(target.GetTimeSinceSeen(), target.GetTraceFraction(), target.GetExposure()))
|
||||
targets.Insert(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user