r2mods/ilspy_dump/ror2_csproj/RoR2/BullseyeSearchOpt.cs

698 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using Grumpy.Util;
using UnityEngine;
namespace RoR2;
public class BullseyeSearchOpt
{
public enum SortMode
{
None,
Distance,
Angle,
DistanceAndAngle
}
private struct CandidateInfo : IEquatable<CandidateInfo>
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct EntityEqualityComparer : IEqualityComparer<CandidateInfo>
{
public bool Equals(CandidateInfo a, CandidateInfo b)
{
return (object)a.hurtBox.healthComponent == b.hurtBox.healthComponent;
}
public int GetHashCode(CandidateInfo obj)
{
return obj.hurtBox.healthComponent.GetHashCode();
}
}
public HurtBox hurtBox;
public Vector3 position;
public Vector3 toTarget;
public float dot;
public float distanceSqr;
public bool Equals(CandidateInfo other)
{
return hurtBox == other.hurtBox;
}
public override bool Equals(object obj)
{
if (obj is CandidateInfo)
{
return hurtBox == ((CandidateInfo)obj).hurtBox;
}
return false;
}
public override int GetHashCode()
{
return hurtBox.GetHashCode();
}
public static bool CandidatesEqual(CandidateInfo a, CandidateInfo b)
{
return a.distanceSqr == b.distanceSqr;
}
}
public CharacterBody viewer;
public Vector3 searchOrigin;
public Vector3 searchDirection;
public float minDistanceFilter;
public float maxDistanceFilter = float.PositiveInfinity;
public TeamMask teamMaskFilter = TeamMask.allButNeutral;
public bool filterByLoS = true;
public bool filterByDistinctEntity;
public QueryTriggerInteraction queryTriggerInteraction;
public BullseyeSearch.SortMode sortMode = BullseyeSearch.SortMode.Distance;
private float minThetaDot = -1f;
private bool InUse;
private List<CandidateInfo> candidates = new List<CandidateInfo>(10);
private Func<CandidateInfo, CandidateInfo, int> CompareDistSqFunc = CompareDistanceSq;
private Func<CandidateInfo, CandidateInfo, int> CompareDistSqAndAngleFunc = CompareDistSqAndAngle;
private Func<CandidateInfo, CandidateInfo, int> CompareAngleFunc = CompareAngle;
private static readonly float fullVisionMinThetaDot = Mathf.Cos(MathF.PI);
private static BullseyeSearchOpt _search = new BullseyeSearchOpt();
public float maxAngleFilter
{
set
{
SetMinAngleDeg(value);
}
}
private bool filterByDistance
{
get
{
if (!(minDistanceFilter > 0f))
{
return maxDistanceFilter < float.PositiveInfinity;
}
return true;
}
}
private bool filterByAngle => minThetaDot > fullVisionMinThetaDot;
public int Count => candidates.Count;
public void SetMinAngleDeg(float inAngle)
{
if (inAngle >= 180f)
{
minThetaDot = fullVisionMinThetaDot;
}
else
{
minThetaDot = Mathf.Cos(inAngle * (MathF.PI / 180f));
}
}
public void RefreshCandidates(bool wantAll = false)
{
int count = HurtBox.readOnlyBullseyesList.Count;
candidates.Clear();
for (int i = 0; i < count; i++)
{
HurtBox hurtBox = HurtBox.readOnlyBullseyesList[i];
CharacterBody characterBody = null;
if (!teamMaskFilter.HasTeam(hurtBox.teamIndex))
{
continue;
}
CandidateInfo item = default(CandidateInfo);
Vector3 position = hurtBox.transform.position;
Vector3 vector = position - searchOrigin;
item.hurtBox = hurtBox;
item.position = position;
item.toTarget = vector;
bool flag = true;
bool flag2 = true;
bool flag3 = true;
float num = minDistanceFilter * minDistanceFilter;
float num2 = maxDistanceFilter * maxDistanceFilter;
float num3 = (item.distanceSqr = vector.sqrMagnitude);
if (filterByDistance)
{
flag = num3 >= num && num3 <= num2;
flag3 = flag3 && flag;
}
if (filterByAngle && flag3)
{
float num4 = Mathf.Sqrt(num3);
Vector3 rhs = vector / num4;
float num5 = Vector3.Dot(searchDirection, rhs);
flag2 = num5 >= minThetaDot;
item.dot = num5;
item.distanceSqr = num3;
flag3 = flag3 && flag2;
}
if (!filterByAngle && flag3 && sortMode == BullseyeSearch.SortMode.Angle)
{
float num6 = Mathf.Sqrt(num3);
Vector3 rhs2 = vector / num6;
float num7 = Vector3.Dot(searchDirection, rhs2);
flag2 = num7 >= minThetaDot;
item.dot = num7;
item.distanceSqr = num3;
}
if (filterByDistinctEntity && flag3)
{
for (int j = 0; j < candidates.Count; j++)
{
if ((object)candidates[j].hurtBox.healthComponent == hurtBox.healthComponent)
{
flag3 = false;
break;
}
}
}
if (flag3 && (bool)viewer)
{
characterBody = hurtBox.healthComponent.body;
flag3 &= characterBody.GetVisibilityLevel(viewer) >= VisibilityLevel.Revealed;
}
if (wantAll && flag3 && filterByLoS)
{
float num8 = Mathf.Sqrt(num3);
Vector3 direction = vector / num8;
RaycastHit hitInfo;
bool flag4 = Physics.Raycast(searchOrigin, direction, out hitInfo, num8, LayerIndex.world.mask, queryTriggerInteraction);
flag3 = flag3 && !flag4;
}
if (flag3)
{
candidates.Add(item);
}
}
switch (sortMode)
{
case BullseyeSearch.SortMode.Angle:
HeapSort<CandidateInfo>.Sort(candidates, CompareAngleFunc);
break;
case BullseyeSearch.SortMode.Distance:
HeapSort<CandidateInfo>.Sort(candidates, CompareDistSqFunc);
break;
case BullseyeSearch.SortMode.DistanceAndAngle:
HeapSort<CandidateInfo>.Sort(candidates, CompareDistSqAndAngleFunc);
break;
}
}
public HurtBox GetClosestCandidate(List<HealthComponent> exclude)
{
ReadOnlyCollection<TeamComponent> teamMembers = TeamComponent.GetTeamMembers((!teamMaskFilter.HasTeam(TeamIndex.Monster)) ? TeamIndex.Player : TeamIndex.Monster);
int count = teamMembers.Count;
CandidateInfo candidateInfo = default(CandidateInfo);
candidateInfo.distanceSqr = float.PositiveInfinity;
bool flag = false;
for (int i = 0; i < count; i++)
{
CharacterBody body = teamMembers[i].body;
if (!exclude.Contains(body.healthComponent))
{
CandidateInfo candidateInfo2 = default(CandidateInfo);
Vector3 corePosition = body.corePosition;
Vector3 vector = corePosition - searchOrigin;
candidateInfo2.hurtBox = body.mainHurtBox;
candidateInfo2.position = corePosition;
bool flag2 = true;
bool flag3 = true;
bool flag4 = true;
float num = minDistanceFilter * minDistanceFilter;
float num2 = maxDistanceFilter * maxDistanceFilter;
float num3 = (candidateInfo2.distanceSqr = vector.sqrMagnitude);
if (filterByDistance)
{
flag2 = num3 >= num && num3 <= num2;
flag4 = flag4 && flag2;
}
if (filterByAngle && flag4)
{
float num4 = Mathf.Sqrt(num3);
Vector3 rhs = vector / num4;
float num5 = Vector3.Dot(searchDirection, rhs);
flag3 = num5 >= minThetaDot;
candidateInfo2.dot = num5;
candidateInfo2.distanceSqr = num3;
flag4 = flag4 && flag3;
}
if (flag4 && (bool)viewer)
{
flag4 &= body.GetVisibilityLevel(viewer) >= VisibilityLevel.Revealed;
}
if (flag4 && filterByLoS)
{
float num6 = Mathf.Sqrt(num3);
Vector3 direction = vector / num6;
RaycastHit hitInfo;
bool flag5 = Physics.Raycast(searchOrigin, direction, out hitInfo, num6, LayerIndex.world.mask, queryTriggerInteraction);
flag4 = flag4 && !flag5;
}
if (flag4 && candidateInfo2.distanceSqr < candidateInfo.distanceSqr)
{
candidateInfo = candidateInfo2;
flag = true;
}
}
}
if (flag)
{
return candidateInfo.hurtBox;
}
return null;
}
public HurtBox GetClosestCandidateFromList(List<HealthComponent> validTargets, HealthComponent prevTarget, out int resultIndex)
{
CandidateInfo candidateInfo = default(CandidateInfo);
candidateInfo.distanceSqr = float.PositiveInfinity;
bool flag = false;
int count = validTargets.Count;
resultIndex = -1;
for (int i = 0; i < count; i++)
{
HealthComponent healthComponent = validTargets[i];
if (!healthComponent || !healthComponent.alive || healthComponent == prevTarget)
{
continue;
}
CharacterBody body = healthComponent.body;
if ((bool)body && (bool)body.mainHurtBox)
{
CandidateInfo candidateInfo2 = default(CandidateInfo);
Vector3 corePosition = body.corePosition;
Vector3 vector = corePosition - searchOrigin;
candidateInfo2.hurtBox = body.mainHurtBox;
candidateInfo2.position = corePosition;
bool flag2 = true;
bool flag3 = true;
bool flag4 = true;
if (flag4 && !candidateInfo2.hurtBox.gameObject.activeInHierarchy)
{
flag4 = false;
}
float num = minDistanceFilter * minDistanceFilter;
float num2 = maxDistanceFilter * maxDistanceFilter;
float num3 = (candidateInfo2.distanceSqr = vector.sqrMagnitude);
if (filterByDistance && flag4)
{
flag2 = num3 >= num && num3 <= num2;
flag4 = flag4 && flag2;
}
if (filterByAngle && flag4)
{
float num4 = Mathf.Sqrt(num3);
Vector3 rhs = vector / num4;
float num5 = Vector3.Dot(searchDirection, rhs);
flag3 = num5 >= minThetaDot;
candidateInfo2.dot = num5;
candidateInfo2.distanceSqr = num3;
flag4 = flag4 && flag3;
}
if (flag4 && (bool)viewer)
{
flag4 &= body.GetVisibilityLevel(viewer) >= VisibilityLevel.Revealed;
}
if (flag4 && filterByLoS)
{
float num6 = Mathf.Sqrt(num3);
Vector3 direction = vector / num6;
RaycastHit hitInfo;
bool flag5 = Physics.Raycast(searchOrigin, direction, out hitInfo, num6, LayerIndex.world.mask, queryTriggerInteraction);
flag4 = flag4 && !flag5;
}
if (flag4 && candidateInfo2.distanceSqr < candidateInfo.distanceSqr)
{
candidateInfo = candidateInfo2;
flag = true;
resultIndex = i;
}
}
}
if (flag)
{
return candidateInfo.hurtBox;
}
return null;
}
public void RefreshCandidates_Cheap()
{
ReadOnlyCollection<TeamComponent> teamMembers = TeamComponent.GetTeamMembers((!teamMaskFilter.HasTeam(TeamIndex.Monster)) ? TeamIndex.Player : TeamIndex.Monster);
int count = teamMembers.Count;
candidates.Clear();
for (int i = 0; i < count; i++)
{
CharacterBody body = teamMembers[i].body;
CandidateInfo item = default(CandidateInfo);
Vector3 corePosition = body.corePosition;
Vector3 vector = corePosition - searchOrigin;
item.hurtBox = body.mainHurtBox;
item.position = corePosition;
item.toTarget = vector;
bool flag = true;
bool flag2 = true;
bool flag3 = true;
float num = minDistanceFilter * minDistanceFilter;
float num2 = maxDistanceFilter * maxDistanceFilter;
float num3 = (item.distanceSqr = vector.sqrMagnitude);
if (filterByDistance)
{
flag = num3 >= num && num3 <= num2;
flag3 = flag3 && flag;
}
if (filterByAngle)
{
float num4 = Mathf.Sqrt(num3);
Vector3 rhs = vector / num4;
float num5 = Vector3.Dot(searchDirection, rhs);
flag2 = num5 >= minThetaDot;
item.dot = num5;
item.distanceSqr = num3;
flag3 = flag3 && flag2;
}
if (flag3 && (bool)viewer)
{
flag3 &= body.GetVisibilityLevel(viewer) >= VisibilityLevel.Revealed;
}
if (flag3 && filterByLoS)
{
float num6 = Mathf.Sqrt(num3);
Vector3 direction = vector / num6;
RaycastHit hitInfo;
bool flag4 = Physics.Raycast(searchOrigin, direction, out hitInfo, num6, LayerIndex.world.mask, queryTriggerInteraction);
flag3 = flag3 && !flag4;
}
if (flag3)
{
candidates.Add(item);
}
}
switch (sortMode)
{
case BullseyeSearch.SortMode.Angle:
HeapSort<CandidateInfo>.Sort(candidates, CompareAngleFunc);
break;
case BullseyeSearch.SortMode.Distance:
HeapSort<CandidateInfo>.Sort(candidates, CompareDistSqFunc);
break;
case BullseyeSearch.SortMode.DistanceAndAngle:
HeapSort<CandidateInfo>.Sort(candidates, CompareDistSqAndAngleFunc);
break;
}
}
public void FilterOutGameObject(GameObject gameObject)
{
for (int num = candidates.Count - 1; num >= 0; num--)
{
if (candidates[num].hurtBox.healthComponent.gameObject == gameObject)
{
candidates.RemoveAt(num);
}
}
}
public void FilterToComponent<T>() where T : Component
{
for (int num = candidates.Count - 1; num >= 0; num--)
{
if (candidates[num].hurtBox.healthComponent.GetComponent<T>() == null)
{
candidates.RemoveAt(num);
}
}
}
public void FilterNotInList(List<HealthComponent> list)
{
for (int num = candidates.Count - 1; num >= 0; num--)
{
HurtBox hurtBox = candidates[num].hurtBox;
if (list.Contains(hurtBox.healthComponent))
{
candidates.RemoveAt(num);
}
}
}
public void FilterInList(List<HealthComponent> list)
{
for (int num = candidates.Count - 1; num >= 0; num--)
{
HurtBox hurtBox = candidates[num].hurtBox;
if (!list.Contains(hurtBox.healthComponent))
{
candidates.RemoveAt(num);
}
}
}
public HurtBox FirstInList(List<HealthComponent> list)
{
HurtBox result = null;
for (int i = 0; i < candidates.Count; i++)
{
HurtBox hurtBox = candidates[i].hurtBox;
if (!list.Contains(hurtBox.healthComponent))
{
result = hurtBox;
break;
}
}
return result;
}
public void FilterCandidatesByHealthFraction(float minHealthFraction = 0f, float maxHealthFraction = 1f)
{
if (minHealthFraction > 0f)
{
if (maxHealthFraction < 1f)
{
for (int num = candidates.Count - 1; num > 0; num--)
{
float combinedHealthFraction = candidates[num].hurtBox.healthComponent.combinedHealthFraction;
if (combinedHealthFraction >= minHealthFraction && !(combinedHealthFraction <= maxHealthFraction))
{
candidates.RemoveAt(num);
}
}
return;
}
for (int num2 = candidates.Count - 1; num2 > 0; num2--)
{
if (!(candidates[num2].hurtBox.healthComponent.combinedHealthFraction >= minHealthFraction))
{
candidates.RemoveAt(num2);
}
}
}
else
{
if (!(maxHealthFraction < 1f))
{
return;
}
for (int num3 = candidates.Count - 1; num3 > 0; num3--)
{
if (!(candidates[num3].hurtBox.healthComponent.combinedHealthFraction <= maxHealthFraction))
{
candidates.RemoveAt(num3);
}
}
}
}
public HurtBox FirstOrDefault()
{
HurtBox result = null;
if (candidates.Count > 0)
{
if (filterByLoS)
{
for (int i = 0; i < candidates.Count; i++)
{
float num = Mathf.Sqrt(candidates[i].distanceSqr);
Vector3 direction = candidates[i].toTarget / num;
if (!Physics.Raycast(searchOrigin, direction, out var _, num, LayerIndex.world.mask, queryTriggerInteraction))
{
return candidates[i].hurtBox;
}
}
}
else
{
result = candidates[0].hurtBox;
}
}
return result;
}
public void DebugPrint()
{
for (int i = 0; i < candidates.Count; i++)
{
Debug.LogError("C" + i + ": " + candidates[i].hurtBox.healthComponent.gameObject.name + ", " + candidates[i].distanceSqr + ", " + candidates[i].dot);
}
}
public HurtBox FirstOrDefaultNotInList(List<HealthComponent> list)
{
HurtBox result = null;
if (candidates.Count > 0)
{
for (int i = 0; i < candidates.Count; i++)
{
HurtBox hurtBox = candidates[i].hurtBox;
if (!list.Contains(hurtBox.healthComponent))
{
return hurtBox;
}
}
}
return result;
}
public HurtBox RandomSelect()
{
HurtBox result = null;
if (candidates.Count > 0)
{
int index = UnityEngine.Random.Range(0, candidates.Count);
result = candidates[index].hurtBox;
}
return result;
}
public void Reset()
{
candidates.Clear();
viewer = null;
searchOrigin = Vector3.zero;
searchDirection = Vector3.zero;
minDistanceFilter = 0f;
maxDistanceFilter = float.PositiveInfinity;
teamMaskFilter = TeamMask.allButNeutral;
filterByLoS = true;
filterByDistinctEntity = false;
queryTriggerInteraction = QueryTriggerInteraction.UseGlobal;
sortMode = BullseyeSearch.SortMode.Distance;
minThetaDot = -1f;
InUse = false;
}
private static float DistSq(Vector3 a, Vector3 b)
{
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) * ((a.z - b.z) * (a.z - b.z));
}
private static int CompareDistanceSq(CandidateInfo left, CandidateInfo right)
{
float num = left.distanceSqr - right.distanceSqr;
if (num < 0f)
{
return -1;
}
if (num > 0f)
{
return 1;
}
return 0;
}
private static int CompareAngle(CandidateInfo left, CandidateInfo right)
{
float num = 0f - left.dot;
float num2 = 0f - right.dot;
float num3 = num - num2;
if (num3 < 0f)
{
return -1;
}
if (num3 > 0f)
{
return 1;
}
return 0;
}
private static int CompareDistSqAndAngle(CandidateInfo left, CandidateInfo right)
{
float num = (0f - left.dot) * left.distanceSqr;
float num2 = (0f - right.dot) * right.distanceSqr;
float num3 = num - num2;
if (num3 < 0f)
{
return -1;
}
if (num3 > 0f)
{
return 1;
}
return 0;
}
public HurtBox GetHurtBox(int idx)
{
return candidates[idx].hurtBox;
}
public static BullseyeSearchOpt FromPool()
{
if (!_search.InUse)
{
_search.InUse = true;
}
else
{
Debug.LogErrorFormat("BullseyeSearch at limit");
}
return _search;
}
public static void ReturnPool(BullseyeSearchOpt inSearch)
{
if (_search == inSearch)
{
_search.Reset();
}
else
{
Debug.LogError("BullseyeSearch in use!");
}
}
}