426 lines
11 KiB
C#
426 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using HG;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace RoR2.UI;
|
|
|
|
[RequireComponent(typeof(RectTransform))]
|
|
public class AllyCardManager : MonoBehaviour, ILayoutGroup, ILayoutController
|
|
{
|
|
private struct SourceInfo
|
|
{
|
|
public readonly GameObject gameObject;
|
|
|
|
public readonly TeamComponent teamComponent;
|
|
|
|
public readonly CharacterMaster master;
|
|
|
|
public SourceInfo(GameObject gameObject)
|
|
{
|
|
bool flag = gameObject;
|
|
this.gameObject = gameObject;
|
|
teamComponent = (flag ? gameObject.GetComponent<TeamComponent>() : null);
|
|
CharacterBody characterBody = (flag ? gameObject.GetComponent<CharacterBody>() : null);
|
|
master = (characterBody ? characterBody.master : null);
|
|
}
|
|
}
|
|
|
|
private struct DisplayElement
|
|
{
|
|
public CharacterMaster master;
|
|
|
|
public bool shouldIndent;
|
|
|
|
public int priority;
|
|
}
|
|
|
|
private class DisplayElementComparer : IComparer<DisplayElement>
|
|
{
|
|
public static DisplayElementComparer instance = new DisplayElementComparer();
|
|
|
|
public int Compare(DisplayElement a, DisplayElement b)
|
|
{
|
|
int num = a.priority - b.priority;
|
|
if (num == 0)
|
|
{
|
|
num = (int)(a.master.netId.Value - b.master.netId.Value);
|
|
}
|
|
return num;
|
|
}
|
|
|
|
private DisplayElementComparer()
|
|
{
|
|
}
|
|
}
|
|
|
|
private struct CharacterData
|
|
{
|
|
public readonly CharacterMaster master;
|
|
|
|
public readonly CharacterMaster leaderMaster;
|
|
|
|
private readonly int masterInstanceId;
|
|
|
|
private readonly int leaderMasterInstanceId;
|
|
|
|
public readonly bool isMinion;
|
|
|
|
public readonly bool isPlayer;
|
|
|
|
public CharacterData(CharacterMaster master)
|
|
{
|
|
this.master = master;
|
|
leaderMaster = master.minionOwnership.ownerMaster;
|
|
isMinion = leaderMaster;
|
|
isPlayer = master.playerCharacterMasterController;
|
|
masterInstanceId = master.gameObject.GetInstanceID();
|
|
leaderMasterInstanceId = (isMinion ? leaderMaster.gameObject.GetInstanceID() : 0);
|
|
}
|
|
|
|
public bool Equals(in CharacterData other)
|
|
{
|
|
if (masterInstanceId == other.masterInstanceId && leaderMasterInstanceId == other.leaderMasterInstanceId && isMinion == other.isMinion)
|
|
{
|
|
return isPlayer == other.isPlayer;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private class CharacterDataSet
|
|
{
|
|
private CharacterData[] buffer = new CharacterData[128];
|
|
|
|
private int _count;
|
|
|
|
public int count => _count;
|
|
|
|
public ref CharacterData this[int i] => ref buffer[i];
|
|
|
|
public bool Equals(CharacterDataSet other)
|
|
{
|
|
if (other == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (_count != other._count)
|
|
{
|
|
return false;
|
|
}
|
|
for (int i = 0; i < _count; i++)
|
|
{
|
|
if (!buffer[i].Equals(in other.buffer[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
Array.Clear(buffer, 0, _count);
|
|
_count = 0;
|
|
}
|
|
|
|
public void Add(ref CharacterData element)
|
|
{
|
|
ArrayUtils.ArrayAppend(ref buffer, ref _count, in element);
|
|
}
|
|
|
|
public void CopyFrom(CharacterDataSet src)
|
|
{
|
|
int num = _count - src._count;
|
|
if (num > 0)
|
|
{
|
|
Array.Clear(buffer, src._count, num);
|
|
}
|
|
ArrayUtils.EnsureCapacity(ref buffer, src.buffer.Length);
|
|
_count = src.count;
|
|
Array.Copy(src.buffer, buffer, _count);
|
|
}
|
|
}
|
|
|
|
private static class CharacterDataSetPool
|
|
{
|
|
private static readonly Stack<CharacterDataSet> pool = new Stack<CharacterDataSet>();
|
|
|
|
public static CharacterDataSet Request()
|
|
{
|
|
if (pool.Count == 0)
|
|
{
|
|
return new CharacterDataSet();
|
|
}
|
|
return pool.Pop();
|
|
}
|
|
|
|
public static void Return(ref CharacterDataSet characterDataSet)
|
|
{
|
|
characterDataSet.Clear();
|
|
pool.Push(characterDataSet);
|
|
}
|
|
}
|
|
|
|
public float indentWidth = 16f;
|
|
|
|
private SourceInfo currentSource;
|
|
|
|
private UIElementAllocator<AllyCardController> cardAllocator;
|
|
|
|
private RectTransform rectTransform;
|
|
|
|
private bool needsRefresh;
|
|
|
|
private DisplayElement[] displayElements = Array.Empty<DisplayElement>();
|
|
|
|
private int displayElementCount;
|
|
|
|
private CharacterDataSet currentCharacterData;
|
|
|
|
private float updateTimer;
|
|
|
|
private const float updateDelay = 0f;
|
|
|
|
public GameObject sourceGameObject
|
|
{
|
|
get
|
|
{
|
|
return currentSource.gameObject;
|
|
}
|
|
set
|
|
{
|
|
if ((object)currentSource.gameObject != value)
|
|
{
|
|
currentSource = new SourceInfo(value);
|
|
OnSourceChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnCardCreated(int index, AllyCardController element)
|
|
{
|
|
Vector2 anchorMin = element.rectTransform.anchorMin;
|
|
anchorMin.x = 0f;
|
|
anchorMin.y = 1f;
|
|
element.rectTransform.anchorMin = anchorMin;
|
|
anchorMin = element.rectTransform.anchorMax;
|
|
anchorMin.x = 1f;
|
|
anchorMin.y = 1f;
|
|
element.rectTransform.anchorMax = anchorMin;
|
|
}
|
|
|
|
private void OnSourceChanged()
|
|
{
|
|
needsRefresh = true;
|
|
}
|
|
|
|
private TeamIndex FindTargetTeam()
|
|
{
|
|
TeamIndex result = TeamIndex.None;
|
|
TeamComponent teamComponent = currentSource.teamComponent;
|
|
if ((bool)teamComponent)
|
|
{
|
|
result = teamComponent.teamIndex;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private void SetCharacterData(CharacterDataSet newCharacterData)
|
|
{
|
|
if (!newCharacterData.Equals(currentCharacterData))
|
|
{
|
|
currentCharacterData.CopyFrom(newCharacterData);
|
|
BuildFromCharacterData(currentCharacterData);
|
|
}
|
|
}
|
|
|
|
private void PopulateCharacterDataSet(CharacterDataSet characterDataSet)
|
|
{
|
|
TeamIndex teamIndex = FindTargetTeam();
|
|
ReadOnlyCollection<CharacterMaster> readOnlyInstancesList = CharacterMaster.readOnlyInstancesList;
|
|
for (int i = 0; i < readOnlyInstancesList.Count; i++)
|
|
{
|
|
CharacterMaster characterMaster = readOnlyInstancesList[i];
|
|
if (characterMaster.teamIndex == teamIndex)
|
|
{
|
|
CharacterBody body = characterMaster.GetBody();
|
|
if ((!body || !body.teamComponent || !body.teamComponent.hideAllyCardDisplay) && (!characterMaster.playerCharacterMasterController || (bool)characterMaster.playerCharacterMasterController.networkUser) && (object)currentSource.master != characterMaster)
|
|
{
|
|
CharacterData element = new CharacterData(characterMaster);
|
|
characterDataSet.Add(ref element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BuildFromCharacterData(CharacterDataSet characterDataSet)
|
|
{
|
|
if (characterDataSet.count < displayElementCount)
|
|
{
|
|
Array.Clear(displayElements, characterDataSet.count, displayElementCount - characterDataSet.count);
|
|
}
|
|
displayElementCount = characterDataSet.count;
|
|
ArrayUtils.EnsureCapacity(ref displayElements, displayElementCount);
|
|
int i = 0;
|
|
for (int count = characterDataSet.count; i < count; i++)
|
|
{
|
|
ref CharacterData reference = ref characterDataSet[i];
|
|
displayElements[i] = new DisplayElement
|
|
{
|
|
master = reference.master,
|
|
priority = -1
|
|
};
|
|
}
|
|
int num = 0;
|
|
int j = 0;
|
|
for (int count2 = characterDataSet.count; j < count2; j++)
|
|
{
|
|
if (characterDataSet[j].isPlayer)
|
|
{
|
|
displayElements[j].priority = num;
|
|
num += 2;
|
|
}
|
|
}
|
|
int k = 0;
|
|
for (int count3 = characterDataSet.count; k < count3; k++)
|
|
{
|
|
if (!characterDataSet[k].isMinion)
|
|
{
|
|
ref DisplayElement reference2 = ref displayElements[k];
|
|
if (reference2.priority == -1)
|
|
{
|
|
reference2.priority = num;
|
|
num += 2;
|
|
}
|
|
}
|
|
}
|
|
int l = 0;
|
|
for (int count4 = characterDataSet.count; l < count4; l++)
|
|
{
|
|
ref CharacterData reference3 = ref characterDataSet[l];
|
|
if (!reference3.isMinion)
|
|
{
|
|
continue;
|
|
}
|
|
ref DisplayElement reference4 = ref displayElements[l];
|
|
if (reference4.priority == -1)
|
|
{
|
|
int num2 = FindIndexForMaster(reference3.leaderMaster);
|
|
if (num2 != -1)
|
|
{
|
|
reference4.priority = displayElements[num2].priority + 1;
|
|
reference4.shouldIndent = true;
|
|
}
|
|
}
|
|
}
|
|
int m = 0;
|
|
for (int count5 = characterDataSet.count; m < count5; m++)
|
|
{
|
|
ref DisplayElement reference5 = ref displayElements[m];
|
|
if (reference5.priority == -1)
|
|
{
|
|
reference5.priority = num;
|
|
num += 2;
|
|
}
|
|
}
|
|
Array.Sort(displayElements, 0, comparer: DisplayElementComparer.instance, length: displayElementCount);
|
|
cardAllocator.AllocateElements(displayElementCount);
|
|
for (int n = 0; n < displayElementCount; n++)
|
|
{
|
|
ref DisplayElement reference6 = ref displayElements[n];
|
|
AllyCardController allyCardController = cardAllocator.elements[n];
|
|
allyCardController.sourceMaster = reference6.master;
|
|
allyCardController.shouldIndent = reference6.shouldIndent;
|
|
}
|
|
ArrayUtils.Clear(displayElements, ref displayElementCount);
|
|
int FindIndexForMaster(CharacterMaster master)
|
|
{
|
|
for (int num3 = 0; num3 < displayElementCount; num3++)
|
|
{
|
|
if ((object)master == displayElements[num3].master)
|
|
{
|
|
return num3;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
cardAllocator = new UIElementAllocator<AllyCardController>((RectTransform)base.transform, LegacyResourcesAPI.Load<GameObject>("Prefabs/UI/AllyCard"));
|
|
rectTransform = (RectTransform)base.transform;
|
|
UIElementAllocator<AllyCardController> uIElementAllocator = cardAllocator;
|
|
uIElementAllocator.onCreateElement = (UIElementAllocator<AllyCardController>.ElementOperationDelegate)Delegate.Combine(uIElementAllocator.onCreateElement, new UIElementAllocator<AllyCardController>.ElementOperationDelegate(OnCardCreated));
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
updateTimer -= Time.deltaTime;
|
|
if (updateTimer <= 0f)
|
|
{
|
|
updateTimer = 0f;
|
|
CharacterDataSet characterDataSet = CharacterDataSetPool.Request();
|
|
PopulateCharacterDataSet(characterDataSet);
|
|
SetCharacterData(characterDataSet);
|
|
CharacterDataSetPool.Return(ref characterDataSet);
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
needsRefresh = true;
|
|
currentCharacterData = CharacterDataSetPool.Request();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
CharacterDataSetPool.Return(ref currentCharacterData);
|
|
}
|
|
|
|
public void SetLayoutHorizontal()
|
|
{
|
|
if (cardAllocator != null)
|
|
{
|
|
ReadOnlyCollection<AllyCardController> elements = cardAllocator.elements;
|
|
int i = 0;
|
|
for (int count = elements.Count; i < count; i++)
|
|
{
|
|
AllyCardController allyCardController = elements[i];
|
|
RectTransform obj = allyCardController.rectTransform;
|
|
Vector2 offsetMin = obj.offsetMin;
|
|
offsetMin.x = (allyCardController.shouldIndent ? indentWidth : 0f);
|
|
obj.offsetMin = offsetMin;
|
|
offsetMin = obj.offsetMax;
|
|
offsetMin.x = 0f;
|
|
obj.offsetMax = offsetMin;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetLayoutVertical()
|
|
{
|
|
if (cardAllocator != null)
|
|
{
|
|
ReadOnlyCollection<AllyCardController> elements = cardAllocator.elements;
|
|
float b = this.rectTransform.rect.height / (float)elements.Count;
|
|
float num = 0f;
|
|
int i = 0;
|
|
for (int count = elements.Count; i < count; i++)
|
|
{
|
|
AllyCardController allyCardController = elements[i];
|
|
RectTransform rectTransform = allyCardController.rectTransform;
|
|
float preferredHeight = allyCardController.layoutElement.preferredHeight;
|
|
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, preferredHeight);
|
|
Vector2 anchoredPosition = rectTransform.anchoredPosition;
|
|
anchoredPosition.y = num;
|
|
rectTransform.anchoredPosition = anchoredPosition;
|
|
num -= Mathf.Min(preferredHeight, b);
|
|
}
|
|
}
|
|
}
|
|
}
|