Decompiled source of CommandQueueIconFix v1.0.3

BepInEx/plugins/CommandQueue.IconFix/CommandQueue.IconFix.dll

Decompiled 11 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using RoR2;
using RoR2.UI;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CommandQueue.NewGameFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("CommandQueue.NewGameFix")]
[assembly: AssemblyTitle("CommandQueue.NewGameFix")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CommandQueueNewGameFix;

[BepInPlugin("com.ror2modding.commandqueue-newgamefix", "CommandQueue.NewGameFix", "1.0.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class NewGameFixPlugin : BaseUnityPlugin
{
	public const string VERSION = "1.0.3";

	private static NewGameFixPlugin instance;

	private static ManualLogSource s_logger;

	private static MethodInfo s_getIconAODR;

	private static bool s_newIconPipeline;

	private static bool s_backfillStarted;

	private static int s_backfilledCount;

	private static bool s_backfillDone;

	private static HUD s_hud;

	private static Transform s_container;

	private static Sprite s_lockIcon;

	private static bool s_repairActive;

	private static readonly HashSet<GameObject> s_asyncAssigned = new HashSet<GameObject>();

	public static NewGameFixPlugin Instance => instance;

	public void Awake()
	{
		instance = this;
		s_logger = ((BaseUnityPlugin)this).Logger;
		DetectFeatures();
		LogInfo(string.Format("Loaded v{0} (async icon pipeline: {1})", "1.0.3", s_newIconPipeline));
	}

	private void DetectFeatures()
	{
		s_getIconAODR = typeof(PickupDef).GetMethod("GetIconAODR", BindingFlags.Instance | BindingFlags.Public, null, new Type[3]
		{
			typeof(bool),
			typeof(Action<Sprite>),
			typeof(Action<Sprite>)
		}, null);
		s_newIconPipeline = s_getIconAODR != null;
	}

	public void OnEnable()
	{
		if (!s_newIconPipeline)
		{
			LogInfo("This game build does not use the async icon pipeline — nothing to fix. Staying idle.");
			return;
		}
		Run.onRunStartGlobal += OnRunStart;
		((MonoBehaviour)this).StartCoroutine(WatchForCommandQueueUI());
	}

	public void OnDisable()
	{
		Run.onRunStartGlobal -= OnRunStart;
	}

	private void OnRunStart(Run run)
	{
		s_backfillStarted = false;
		s_backfillDone = false;
		s_backfilledCount = 0;
		s_asyncAssigned.Clear();
		((MonoBehaviour)this).StartCoroutine(EnsureBackfill());
		((MonoBehaviour)this).StartCoroutine(WatchForCommandQueueUI());
	}

	private IEnumerator EnsureBackfill()
	{
		if (s_backfillStarted)
		{
			yield break;
		}
		s_backfillStarted = true;
		Queue<PickupDef> todo = new Queue<PickupDef>();
		foreach (PickupDef allPickup in PickupCatalog.allPickups)
		{
			if (allPickup != null && !Object.op_Implicit((Object)(object)allPickup.iconSprite))
			{
				todo.Enqueue(allPickup);
			}
		}
		LogInfo($"Backfill: {todo.Count} pickup(s) without a direct icon sprite");
		int perFrame = 32;
		while (todo.Count > 0)
		{
			for (int i = 0; i < perFrame; i++)
			{
				if (todo.Count <= 0)
				{
					break;
				}
				if (TryResolveIcon(todo.Dequeue()))
				{
					s_backfilledCount++;
				}
			}
			yield return null;
		}
		s_backfillDone = true;
		LogInfo($"Backfill done: resolved {s_backfilledCount} icon(s)");
		if (FindContainer())
		{
			((MonoBehaviour)this).StartCoroutine(RepairPass());
		}
	}

	private static bool TryResolveIcon(PickupDef def)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		try
		{
			object obj = s_getIconAODR.Invoke(def, new object[3] { true, null, null });
			if (obj == null)
			{
				return false;
			}
			object obj2 = (object)(Object)obj.GetType().GetMethod("WaitForCompletion").Invoke(obj, null);
			Sprite val = (Sprite)((obj2 is Sprite) ? obj2 : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				def.iconSprite = val;
				return true;
			}
			return false;
		}
		catch (Exception ex)
		{
			LogWarn("Failed to resolve icon for '" + def?.internalName + "': " + ex.Message);
			return false;
		}
	}

	private IEnumerator WatchForCommandQueueUI()
	{
		int attempts = 0;
		while (!Object.op_Implicit((Object)(object)s_hud) && attempts++ < 120)
		{
			HUD val = Object.FindObjectOfType<HUD>();
			if ((Object)(object)val != (Object)null)
			{
				s_hud = val;
			}
			else
			{
				yield return (object)new WaitForSeconds(1f);
			}
		}
		if (!Object.op_Implicit((Object)(object)s_hud))
		{
			LogInfo("No HUD found — CommandQueue UI is not active, repair pass disabled.");
			yield break;
		}
		attempts = 0;
		while (!FindContainer() && attempts++ < 240)
		{
			yield return (object)new WaitForSeconds(0.5f);
		}
		if (FindContainer())
		{
			LogInfo("CommandQueue container found, starting icon repair.");
			((MonoBehaviour)this).StartCoroutine(RepairPass());
		}
	}

	private bool FindContainer()
	{
		if ((Object)(object)s_container != (Object)null)
		{
			return true;
		}
		GameObject val = GameObject.Find("CommandQueueContainer");
		if ((Object)(object)val != (Object)null)
		{
			s_container = val.transform;
			return true;
		}
		if ((Object)(object)s_hud != (Object)null && FindChildByName(((Component)s_hud).transform, "CommandQueueContainer", out var result))
		{
			s_container = result;
			return true;
		}
		return false;
	}

	private IEnumerator RepairPass()
	{
		if (s_repairActive)
		{
			yield break;
		}
		s_repairActive = true;
		int totalFixed = 0;
		try
		{
			int idleFrames = 0;
			for (int i = 0; i < 14400; i++)
			{
				if (!FindContainer())
				{
					s_container = null;
					yield return (object)new WaitForSeconds(1f);
					continue;
				}
				int num = RepairAllIcons();
				totalFixed += num;
				if (num == 0)
				{
					int num2 = idleFrames + 1;
					idleFrames = num2;
					if (num2 > 30)
					{
						break;
					}
					yield return null;
				}
				else
				{
					idleFrames = 0;
					yield return null;
				}
			}
		}
		finally
		{
			s_repairActive = false;
		}
		LogInfo($"IconFix repair finished: {totalFixed} button icon(s) assigned");
	}

	private int RepairAllIcons()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_018d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Expected O, but got Unknown
		int num = 0;
		foreach (Transform item in s_container)
		{
			Transform val = item;
			if (!((Object)val).name.EndsWith("QueueContainer") || !Enum.TryParse<ItemTier>(((Object)val).name.Substring(0, ((Object)val).name.Length - "QueueContainer".Length), out ItemTier result))
			{
				continue;
			}
			Transform val2 = val;
			int num2 = 0;
			foreach (Transform item2 in val)
			{
				Transform val3 = item2;
				if (!((Object)(object)((Component)val3).GetComponent<GridLayoutGroup>() == (Object)null))
				{
					int num3 = ((Component)val3).GetComponentsInChildren<MPButton>(true).Length;
					if (num3 > num2)
					{
						num2 = num3;
						val2 = val3;
					}
				}
			}
			List<Image> list = null;
			try
			{
				MPButton[] componentsInChildren = ((Component)val2).GetComponentsInChildren<MPButton>(true);
				if (componentsInChildren.Length != 0)
				{
					list = new List<Image>();
					MPButton[] array = componentsInChildren;
					foreach (MPButton val4 in array)
					{
						if (!((Object)(object)((Component)val4).transform.parent == (Object)(object)val))
						{
							Image buttonIconImage = GetButtonIconImage(val4);
							if ((Object)(object)buttonIconImage != (Object)null)
							{
								list.Add(buttonIconImage);
							}
						}
					}
				}
			}
			catch
			{
				list = null;
			}
			if (list == null || list.Count == 0)
			{
				list = new List<Image>();
				CollectIconImages(val, list);
			}
			if (list.Count == 0)
			{
				continue;
			}
			PickupIndex[] itemOptionsForTier = GetItemOptionsForTier(result);
			if (itemOptionsForTier == null || itemOptionsForTier.Length == 0)
			{
				continue;
			}
			try
			{
				int num4 = itemOptionsForTier.Length - itemOptionsForTier.Distinct().Count();
				PickupDef pickupDef = PickupCatalog.GetPickupDef(itemOptionsForTier[0]);
				PickupDef pickupDef2 = PickupCatalog.GetPickupDef(itemOptionsForTier[^1]);
				LogInfo($"Tier {result}: grid images={list.Count}, items={itemOptionsForTier.Length}" + ((num4 > 0) ? $"  *** {num4} duplicate(s) in the item list ***" : "") + ", first='" + pickupDef?.internalName + "', last='" + pickupDef2?.internalName + "'");
			}
			catch
			{
			}
			for (int j = 0; j < list.Count && j < itemOptionsForTier.Length; j++)
			{
				Image val5 = list[j];
				if (!((Object)(object)val5 == (Object)null) && !((Object)(object)val5.sprite != (Object)null))
				{
					PickupDef pickupDef3 = PickupCatalog.GetPickupDef(itemOptionsForTier[j]);
					if (RepairOneButton(val5, pickupDef3))
					{
						num++;
					}
				}
			}
		}
		return num;
	}

	private static Image GetButtonIconImage(MPButton button)
	{
		try
		{
			ChildLocator component = ((Component)button).GetComponent<ChildLocator>();
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			Transform obj = component.FindChild("Icon");
			return (obj != null) ? ((Component)obj).GetComponent<Image>() : null;
		}
		catch
		{
			return null;
		}
	}

	private static bool RepairOneButton(Image img, PickupDef pickupDef)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		if (pickupDef == null)
		{
			return false;
		}
		if (!(((Graphic)img).color.g < 0.6f) && (!(Mathf.Abs(((Graphic)img).color.r - ((Graphic)img).color.b) < 0.01f) || !(((Graphic)img).color.r < 0.7f)))
		{
			GameObject gameObject = ((Component)img).gameObject;
			AsyncImage val = default(AsyncImage);
			if (gameObject.TryGetComponent<AsyncImage>(ref val))
			{
				if (s_asyncAssigned.Contains(gameObject))
				{
					return false;
				}
				((AsyncViewable<Sprite, Image>)(object)val).SetAssetOrDirectReference(pickupDef.iconSpriteReference, pickupDef.iconSprite);
				s_asyncAssigned.Add(gameObject);
				return true;
			}
			if (s_backfillDone || ResolveIconNow(pickupDef))
			{
				Sprite iconSprite = pickupDef.iconSprite;
				if ((Object)(object)iconSprite != (Object)null)
				{
					img.sprite = iconSprite;
					return true;
				}
			}
			return false;
		}
		Sprite lockIcon = GetLockIcon();
		if ((Object)(object)lockIcon != (Object)null)
		{
			img.sprite = lockIcon;
			return true;
		}
		return false;
	}

	private static bool ResolveIconNow(PickupDef def)
	{
		if (!s_newIconPipeline)
		{
			return false;
		}
		return TryResolveIcon(def);
	}

	private static Sprite GetLockIcon()
	{
		if ((Object)(object)s_lockIcon != (Object)null)
		{
			return s_lockIcon;
		}
		try
		{
			s_lockIcon = LegacyResourcesAPI.Load<Sprite>("Textures/MiscIcons/texUnlockIcon");
		}
		catch (Exception ex)
		{
			LogWarn("Could not load lock icon: " + ex.Message);
		}
		return s_lockIcon;
	}

	private static PickupIndex[] GetItemOptionsForTier(ItemTier tier)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			PickupIndex[] groupFromPickupIndex = PickupTransmutationManager.GetGroupFromPickupIndex(PickupCatalog.FindPickupIndex(tier));
			if (groupFromPickupIndex != null && groupFromPickupIndex.Length != 0)
			{
				return groupFromPickupIndex;
			}
			return (from itemDef in ((IEnumerable<ItemIndex>)(object)ItemCatalog.allItems).Select((Func<ItemIndex, ItemDef>)ItemCatalog.GetItemDef)
				where (Object)(object)itemDef != (Object)null && itemDef.tier == tier
				let pickupIndex = PickupCatalog.FindPickupIndex(itemDef.itemIndex)
				where pickupIndex != PickupIndex.none
				select pickupIndex).ToArray();
		}
		catch (Exception ex)
		{
			LogWarn($"GetItemOptionsForTier({tier}) failed: {ex.Message}");
			return null;
		}
	}

	private static void CollectIconImages(Transform root, List<Image> result)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		foreach (Transform item in root)
		{
			Transform val = item;
			if (((Object)val).name == "Icon")
			{
				Image component = ((Component)val).GetComponent<Image>();
				if ((Object)(object)component != (Object)null)
				{
					result.Add(component);
				}
			}
			CollectIconImages(val, result);
		}
	}

	private static bool FindChildByName(Transform root, string name, out Transform result)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		result = null;
		foreach (Transform item in root)
		{
			Transform val = item;
			if (((Object)val).name == name)
			{
				result = val;
				return true;
			}
			if (FindChildByName(val, name, out result))
			{
				return true;
			}
		}
		return false;
	}

	private static void LogInfo(string msg)
	{
		ManualLogSource obj = s_logger;
		if (obj != null)
		{
			obj.LogInfo((object)msg);
		}
	}

	private static void LogWarn(string msg)
	{
		ManualLogSource obj = s_logger;
		if (obj != null)
		{
			obj.LogWarning((object)msg);
		}
	}
}