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 System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using RoR2; using UnityEngine; [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("QualitiesHUDdleUPCompat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("QualitiesHUDdleUPCompat")] [assembly: AssemblyTitle("QualitiesHUDdleUPCompat")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace QualitiesHUDdleUPCompat { [BepInPlugin("com.dbossofdworld.qualitieshuddleupcompat", "Qualities HUDdleUP Compat", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private enum DisplayTiming { Always, AfterTeleporterBossDefeated, AfterTeleporterCharged, Never } private Harmony harmony; private static ConfigEntry enableQualityChestLabel; private static ConfigEntry enableQualityCloakedChestLabel; private static ConfigEntry enableQualityEquipmentBarrelLabel; private static ConfigEntry enableQualityPrinterLabel; private static ConfigEntry qualityChestDisplayTiming; private static ConfigEntry qualityCloakedChestDisplayTiming; private static ConfigEntry qualityEquipmentBarrelDisplayTiming; private static ConfigEntry qualityPrinterDisplayTiming; private static ConfigEntry qualityChestLabel; private static ConfigEntry qualityCloakedChestLabel; private static ConfigEntry qualityEquipmentBarrelLabel; private static ConfigEntry qualityPrinterLabel; private static ConfigEntry qualityChestLabelColor; private static ConfigEntry qualityCloakedChestLabelColor; private static ConfigEntry qualityEquipmentBarrelLabelColor; private static ConfigEntry qualityPrinterLabelColor; private static ConfigEntry combineQualityChestWithNormalChest; private static ConfigEntry combineQualityEquipmentBarrelWithNormalEquipmentBarrel; private static ConfigEntry enableDebugLogging; private static int qualityChests; private static int qualityChestsAvailable; private static int qualityCloakedChests; private static int qualityCloakedChestsAvailable; private static int qualityEquipmentBarrels; private static int qualityEquipmentBarrelsAvailable; private static int qualityPrinters; private static int qualityPrintersAvailable; private static MethodInfo hudFormatLabelMethod; private static MethodInfo hudFormatCounterMethod; public void Awake() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown BindConfig(); harmony = new Harmony("com.dbossofdworld.qualitieshuddleupcompat"); try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name.Equals("HUDdleUP", StringComparison.OrdinalIgnoreCase)); if (assembly == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"HUDdleUP assembly not found. Compat patch skipped."); return; } PatchInteractablesConstructor(assembly); PatchLootPanelText(assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Qualities HUDdleUP Compat 1.0.0 loaded."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void BindConfig() { enableQualityChestLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Enable", "Enable Quality Chest", true, "If true, shows the Quality Chest line on HUDdleUP's loot panel."); enableQualityCloakedChestLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Enable", "Enable Quality Cloaked Chest", true, "If true, shows the Quality Cloaked Chest line on HUDdleUP's loot panel."); enableQualityEquipmentBarrelLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Enable", "Enable Quality Equipment Barrel", true, "If true, shows the Quality Equipment Barrel line on HUDdleUP's loot panel."); enableQualityPrinterLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Enable", "Enable Quality 3D Printer", true, "If true, shows the Quality 3D Printer line on HUDdleUP's loot panel."); qualityChestDisplayTiming = ((BaseUnityPlugin)this).Config.Bind("Labels - Timing", "Quality Chest Timing", DisplayTiming.Always, "When the Quality Chest line should appear. Options: Always, AfterTeleporterBossDefeated, AfterTeleporterCharged, Never."); qualityCloakedChestDisplayTiming = ((BaseUnityPlugin)this).Config.Bind("Labels - Timing", "Quality Cloaked Chest Timing", DisplayTiming.AfterTeleporterBossDefeated, "When the Quality Cloaked Chest line should appear. Options: Always, AfterTeleporterBossDefeated, AfterTeleporterCharged, Never."); qualityEquipmentBarrelDisplayTiming = ((BaseUnityPlugin)this).Config.Bind("Labels - Timing", "Quality Equipment Barrel Timing", DisplayTiming.Always, "When the Quality Equipment Barrel line should appear. Options: Always, AfterTeleporterBossDefeated, AfterTeleporterCharged, Never."); qualityPrinterDisplayTiming = ((BaseUnityPlugin)this).Config.Bind("Labels - Timing", "Quality 3D Printer Timing", DisplayTiming.AfterTeleporterBossDefeated, "When the Quality 3D Printer line should appear. Options: Always, AfterTeleporterBossDefeated, AfterTeleporterCharged, Never."); qualityChestLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Text", "Quality Chest Label", "Quality Chest", "Text used for the Quality Chest line."); qualityCloakedChestLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Text", "Quality Cloaked Chest Label", "Quality Cloaked Chest", "Text used for the Quality Cloaked Chest line."); qualityEquipmentBarrelLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Text", "Quality Equipment Barrel Label", "Quality Equipment Barrel", "Text used for the Quality Equipment Barrel line."); qualityPrinterLabel = ((BaseUnityPlugin)this).Config.Bind("Labels - Text", "Quality 3D Printer Label", "Quality 3D Printer", "Text used for the Quality 3D Printer line."); qualityChestLabelColor = ((BaseUnityPlugin)this).Config.Bind("Labels - Color", "Quality Chest Label Color", "#FFFFFFFF", "Hex color for the Quality Chest label. Example: #FFFFFFFF for white."); qualityCloakedChestLabelColor = ((BaseUnityPlugin)this).Config.Bind("Labels - Color", "Quality Cloaked Chest Label Color", "#FFFFFFFF", "Hex color for the Quality Cloaked Chest label. Example: #FFFFFFFF for white."); qualityEquipmentBarrelLabelColor = ((BaseUnityPlugin)this).Config.Bind("Labels - Color", "Quality Equipment Barrel Label Color", "#FFFFFFFF", "Hex color for the Quality Equipment Barrel label. Example: #FFFFFFFF for white."); qualityPrinterLabelColor = ((BaseUnityPlugin)this).Config.Bind("Labels - Color", "Quality 3D Printer Label Color", "#FFFFFFFF", "Hex color for the Quality 3D Printer label. Example: #FFFFFFFF for white."); combineQualityChestWithNormalChest = ((BaseUnityPlugin)this).Config.Bind("Formatting", "Combine Quality Chest With Normal Chest", false, "If true, Quality Chest counts are added to HUDdleUP's normal Chest line instead of showing a separate Quality Chest line."); combineQualityEquipmentBarrelWithNormalEquipmentBarrel = ((BaseUnityPlugin)this).Config.Bind("Formatting", "Combine Quality Equipment Barrel With Normal Equipment Barrel", false, "If true, Quality Equipment Barrel counts are added to HUDdleUP's normal Equipment Barrel line instead of showing a separate Quality Equipment Barrel line."); enableDebugLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "Enable Debug Logging", false, "If true, logs possible Quality interactables seen by the compatibility mod."); } private void PatchInteractablesConstructor(Assembly hudAsm) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown Type type = hudAsm.GetType("HUDdleUP.Loot.Interactables"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find HUDdleUP.Loot.Interactables. HUDdleUP may have changed its loot internals."); return; } ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(List) }, null); if (constructor == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Interactables(List). HUDdleUP may have changed its loot internals."); return; } harmony.Patch((MethodBase)constructor, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin).GetMethod("InteractablesCtorPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched HUDdleUP.Loot.Interactables constructor for separate Quality loot counts."); } private void PatchLootPanelText(Assembly hudAsm) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown Type type = hudAsm.GetType("HUDdleUP.Loot.LootPanel"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find HUDdleUP.Loot.LootPanel. Separate Quality loot lines will not be displayed."); return; } MethodInfo methodInfo = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GenerateText" && m.ReturnType == typeof(string)); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find LootPanel.GenerateText returning string. Separate Quality loot lines will not be displayed."); return; } hudFormatLabelMethod = type.GetMethod("FormatLabel", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); hudFormatCounterMethod = type.GetMethod("FormatCounter", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(int), typeof(int) }, null); if (hudFormatLabelMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find HUDdleUP FormatLabel method. Falling back to local Quality line formatting."); } if (hudFormatCounterMethod == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find HUDdleUP FormatCounter method. Falling back to local Quality counter formatting."); } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin).GetMethod("GenerateTextPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched HUDdleUP.Loot.LootPanel.GenerateText for separate Quality loot lines."); } private static void InteractablesCtorPostfix(object __instance, List interactions) { if (__instance == null || interactions == null) { return; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; foreach (PurchaseInteraction interaction in interactions) { if ((Object)(object)interaction == (Object)null) { continue; } string text = interaction.displayNameToken ?? ""; string text2 = (Object.op_Implicit((Object)(object)((Component)interaction).gameObject) ? (((Object)((Component)interaction).gameObject).name ?? "") : ""); if (enableDebugLogging != null && enableDebugLogging.Value && LooksQualityRelated(text, text2)) { Debug.Log((object)$"[QualitiesHUDdleUPCompat] Saw possible Quality interactable: token={text}, object={text2}, available={interaction.available}"); } if (IsQualityPrinter(text, text2)) { num7++; if (interaction.available) { num8++; } } else if (IsQualityEquipmentBarrel(text, text2)) { num5++; if (interaction.available) { num6++; } } else { if (!IsQualityChest(text, text2)) { continue; } if (IsCloaked(text, text2)) { num3++; if (interaction.available) { num4++; } } else { num++; if (interaction.available) { num2++; } } } } qualityChests = num; qualityChestsAvailable = num2; qualityCloakedChests = num3; qualityCloakedChestsAvailable = num4; qualityEquipmentBarrels = num5; qualityEquipmentBarrelsAvailable = num6; qualityPrinters = num7; qualityPrintersAvailable = num8; } private static void GenerateTextPostfix(ref string __result) { if (string.IsNullOrEmpty(__result)) { return; } List list = __result.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n') .ToList(); bool flag = false; bool flag2 = false; if (ShouldCombineQualityChestWithNormalChest()) { flag = TryMergeQualityCountIntoExistingLine(list, (string line) => ContainsIgnoreCase(line, "Chest") && !ContainsIgnoreCase(line, "Quality") && !ContainsIgnoreCase(line, "Cloaked") && !ContainsIgnoreCase(line, "Legendary") && !ContainsIgnoreCase(line, "Equipment"), "Chest", qualityChestsAvailable, qualityChests); } if (ShouldCombineQualityEquipmentBarrelWithNormalEquipmentBarrel()) { flag2 = TryMergeQualityCountIntoExistingLine(list, (string line) => ContainsIgnoreCase(line, "Equipment Barrel") && !ContainsIgnoreCase(line, "Quality"), "Equipment Barrel", qualityEquipmentBarrelsAvailable, qualityEquipmentBarrels); } List list2 = BuildQualityOpenableLootLines(flag, flag2); List list3 = BuildQualityPrinterLootLines(); if (list2.Count != 0 || list3.Count != 0 || flag || flag2) { if (list2.Count > 0) { InsertQualityOpenableLines(list, list2); } if (list3.Count > 0) { InsertQualityPrinterLines(list, list3); } __result = string.Join("\n", list); } } private static bool ShouldCombineQualityChestWithNormalChest() { if (combineQualityChestWithNormalChest != null && combineQualityChestWithNormalChest.Value && enableQualityChestLabel != null && enableQualityChestLabel.Value && qualityChests > 0) { return ShouldDisplay(qualityChestDisplayTiming.Value); } return false; } private static bool ShouldCombineQualityEquipmentBarrelWithNormalEquipmentBarrel() { if (combineQualityEquipmentBarrelWithNormalEquipmentBarrel != null && combineQualityEquipmentBarrelWithNormalEquipmentBarrel.Value && enableQualityEquipmentBarrelLabel != null && enableQualityEquipmentBarrelLabel.Value && qualityEquipmentBarrels > 0) { return ShouldDisplay(qualityEquipmentBarrelDisplayTiming.Value); } return false; } private static bool TryMergeQualityCountIntoExistingLine(List lines, Func linePredicate, string normalLabel, int qualityAvailable, int qualityTotal) { if (lines == null || linePredicate == null || qualityTotal <= 0) { return false; } int num = FindLineIndex(lines, linePredicate); if (num < 0) { return false; } string text = lines[num]; Match match = Regex.Match(StripRichTextTags(text), "(?\\d+)\\s*/\\s*(?\\d+)"); if (!match.Success) { return false; } if (!int.TryParse(match.Groups["available"].Value, out var result)) { return false; } if (!int.TryParse(match.Groups["total"].Value, out var result2)) { return false; } string value = GetOriginalLinePrefixBeforeCounter(text); if (string.IsNullOrEmpty(value)) { value = FormatHudLabel(normalLabel); } value = CloseUnclosedRichTextTags(value); lines[num] = value + FormatHudCounter(result + qualityAvailable, result2 + qualityTotal); return true; } private static string GetOriginalLinePrefixBeforeCounter(string line) { if (string.IsNullOrEmpty(line)) { return string.Empty; } int num = line.LastIndexOf(':'); if (num < 0) { return string.Empty; } int i; for (i = num + 1; i < line.Length && char.IsWhiteSpace(line[i]); i++) { } return line.Substring(0, i); } private static string CloseUnclosedRichTextTags(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(value); int count = Regex.Matches(value, ")", RegexOptions.IgnoreCase).Count; for (int i = Regex.Matches(value, "", RegexOptions.IgnoreCase).Count; i < count; i++) { stringBuilder.Append(""); } int count2 = Regex.Matches(value, ")", RegexOptions.IgnoreCase).Count; for (int j = Regex.Matches(value, "", RegexOptions.IgnoreCase).Count; j < count2; j++) { stringBuilder.Append(""); } return stringBuilder.ToString(); } private static string StripRichTextTags(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return Regex.Replace(value, "<.*?>", string.Empty); } private static void InsertQualityOpenableLines(List lines, List qualityLines) { int num = FindFirstUtilityLineIndex(lines); if (num >= 0) { int index = num; if (num > 0 && string.IsNullOrWhiteSpace(lines[num - 1])) { index = num - 1; } lines.InsertRange(index, qualityLines); return; } int num2 = FindLineIndex(lines, (string line) => ContainsIgnoreCase(line, "Enemies")); int num3 = FindLineIndex(lines, IsStageHeaderLine); int num4 = ((num2 >= 0) ? num2 : num3); if (num4 >= 0) { if (num4 > 0 && string.IsNullOrWhiteSpace(lines[num4 - 1])) { num4--; lines.RemoveAt(num4); } lines.InsertRange(num4, qualityLines); int num5 = num4 + qualityLines.Count; if (num5 >= lines.Count || !string.IsNullOrWhiteSpace(lines[num5])) { lines.Insert(num5, string.Empty); } } else { lines.AddRange(qualityLines); } } private static void InsertQualityPrinterLines(List lines, List qualityLines) { int num = FindLineIndex(lines, (string line) => ContainsIgnoreCase(line, "Enemies")); int num2 = FindLineIndex(lines, IsStageHeaderLine); int num3 = ((num >= 0) ? num : num2); int num4 = FindLastUtilityLineIndex(lines, num3); if (num4 >= 0) { lines.InsertRange(num4 + 1, qualityLines); } else if (num3 >= 0) { if (num3 > 0 && string.IsNullOrWhiteSpace(lines[num3 - 1])) { num3--; lines.RemoveAt(num3); } lines.InsertRange(num3, qualityLines); int num5 = num3 + qualityLines.Count; if (num5 >= lines.Count || !string.IsNullOrWhiteSpace(lines[num5])) { lines.Insert(num5, string.Empty); } } else { lines.AddRange(qualityLines); } } private static int FindFirstUtilityLineIndex(List lines) { return FindLineIndex(lines, (string line) => !ContainsIgnoreCase(line, "Quality") && (ContainsIgnoreCase(line, "Scrapper") || ContainsIgnoreCase(line, "Printer"))); } private static int FindLastUtilityLineIndex(List lines, int boundaryIndex) { int num = ((boundaryIndex >= 0) ? boundaryIndex : lines.Count); int result = -1; for (int i = 0; i < num; i++) { string value = lines[i]; if (!ContainsIgnoreCase(value, "Quality") && (ContainsIgnoreCase(value, "Scrapper") || ContainsIgnoreCase(value, "Printer"))) { result = i; } } return result; } private static int FindLineIndex(List lines, Func predicate) { for (int i = 0; i < lines.Count; i++) { if (predicate(lines[i])) { return i; } } return -1; } private static bool IsStageHeaderLine(string line) { return line?.TrimStart().StartsWith("-- ", StringComparison.OrdinalIgnoreCase) ?? false; } private static bool ContainsIgnoreCase(string value, string search) { if (value != null) { return value.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static List BuildQualityOpenableLootLines(bool qualityChestWasCombined, bool qualityEquipmentWasCombined) { List list = new List(); if (!qualityChestWasCombined && enableQualityChestLabel.Value && qualityChests > 0 && ShouldDisplay(qualityChestDisplayTiming.Value)) { bool flag = ShouldCombineQualityChestWithNormalChest(); list.Add(flag ? FormatNormalAvailableLine("Chest", qualityChestsAvailable, qualityChests) : FormatAvailableLine(qualityChestLabel.Value, qualityChestLabelColor.Value, qualityChestsAvailable, qualityChests)); } if (enableQualityCloakedChestLabel.Value && qualityCloakedChests > 0 && ShouldDisplay(qualityCloakedChestDisplayTiming.Value)) { list.Add(FormatAvailableLine(qualityCloakedChestLabel.Value, qualityCloakedChestLabelColor.Value, qualityCloakedChestsAvailable, qualityCloakedChests)); } if (!qualityEquipmentWasCombined && enableQualityEquipmentBarrelLabel.Value && qualityEquipmentBarrels > 0 && ShouldDisplay(qualityEquipmentBarrelDisplayTiming.Value)) { bool flag2 = ShouldCombineQualityEquipmentBarrelWithNormalEquipmentBarrel(); list.Add(flag2 ? FormatNormalAvailableLine("Equipment Barrel", qualityEquipmentBarrelsAvailable, qualityEquipmentBarrels) : FormatAvailableLine(qualityEquipmentBarrelLabel.Value, qualityEquipmentBarrelLabelColor.Value, qualityEquipmentBarrelsAvailable, qualityEquipmentBarrels)); } return list; } private static List BuildQualityPrinterLootLines() { List list = new List(); if (enableQualityPrinterLabel.Value && qualityPrinters > 0 && ShouldDisplay(qualityPrinterDisplayTiming.Value)) { list.Add(FormatTotalOnlyLine(qualityPrinterLabel.Value, qualityPrinterLabelColor.Value, qualityPrinters)); } return list; } private static bool ShouldDisplay(DisplayTiming timing) { switch (timing) { case DisplayTiming.Always: return true; case DisplayTiming.AfterTeleporterBossDefeated: return IsTeleporterBossDefeated(); case DisplayTiming.AfterTeleporterCharged: if ((Object)(object)TeleporterInteraction.instance != (Object)null) { return TeleporterInteraction.instance.isCharged; } return false; default: return false; } } private static bool IsTeleporterBossDefeated() { TeleporterInteraction instance = TeleporterInteraction.instance; if ((Object)(object)instance == (Object)null) { return false; } if (instance.isCharged) { return true; } try { object memberValue = GetMemberValue(instance, "bossGroup"); if (memberValue == null) { memberValue = GetMemberValue(instance, "currentBossGroup"); } if (memberValue == null) { return false; } if (GetMemberValue(memberValue, "isDefeated") is bool result) { return result; } if (GetMemberValue(memberValue, "livingMembers") is int num) { return num <= 0; } if (GetMemberValue(memberValue, "membersList") is ICollection collection) { return collection.Count == 0; } } catch { } return false; } private static object GetMemberValue(object instance, string memberName) { if (instance == null || string.IsNullOrEmpty(memberName)) { return null; } Type type = instance.GetType(); FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0) { return property.GetValue(instance, null); } return null; } private static string FormatNormalAvailableLine(string label, int available, int total) { return FormatHudLabel(label) + FormatHudCounter(available, total); } private static string FormatAvailableLine(string label, string color, int available, int total) { return FormatHudLabel(WrapColor(label, color)) + FormatHudCounter(available, total); } private static string FormatTotalOnlyLine(string label, string color, int total) { return FormatHudLabel(WrapColor(label, color)) + total; } private static string FormatHudLabel(string formattedLabel) { try { if (hudFormatLabelMethod != null) { return (string)hudFormatLabelMethod.Invoke(null, new object[1] { formattedLabel }); } } catch { } return "> " + formattedLabel + ": "; } private static string FormatHudCounter(int available, int total) { try { if (hudFormatCounterMethod != null) { return (string)hudFormatCounterMethod.Invoke(null, new object[2] { available, total }); } } catch { } return $"{available}/{total}"; } private static string WrapColor(string label, string color) { if (string.IsNullOrWhiteSpace(label)) { label = "Quality"; } string text = NormalizeColor(color); return "" + label + ""; } private static string NormalizeColor(string color) { if (string.IsNullOrWhiteSpace(color)) { return "#FFFFFFFF"; } color = color.Trim(); if (!color.StartsWith("#")) { color = "#" + color; } if (color.Length == 4 || color.Length == 7 || color.Length == 9) { return color; } return "#FFFFFFFF"; } private static bool LooksQualityRelated(string token, string objectName) { if (token.IndexOf("QUALITY", StringComparison.OrdinalIgnoreCase) < 0 && objectName.IndexOf("Quality", StringComparison.OrdinalIgnoreCase) < 0) { return objectName.IndexOf("ItemQualities", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static bool IsQualityChest(string token, string objectName) { if (LooksQualityRelated(token, objectName)) { if (token.IndexOf("CHEST", StringComparison.OrdinalIgnoreCase) < 0) { return objectName.IndexOf("Chest", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsQualityEquipmentBarrel(string token, string objectName) { if (LooksQualityRelated(token, objectName)) { if (token.IndexOf("EQUIPMENT", StringComparison.OrdinalIgnoreCase) < 0 && objectName.IndexOf("Equipment", StringComparison.OrdinalIgnoreCase) < 0) { return objectName.IndexOf("Barrel", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsQualityPrinter(string token, string objectName) { if (LooksQualityRelated(token, objectName)) { if (token.IndexOf("DUPLICATOR", StringComparison.OrdinalIgnoreCase) < 0 && token.IndexOf("PRINTER", StringComparison.OrdinalIgnoreCase) < 0 && objectName.IndexOf("Duplicator", StringComparison.OrdinalIgnoreCase) < 0) { return objectName.IndexOf("Printer", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return false; } private static bool IsCloaked(string token, string objectName) { if (token.IndexOf("STEALTHED", StringComparison.OrdinalIgnoreCase) < 0 && objectName.IndexOf("Stealthed", StringComparison.OrdinalIgnoreCase) < 0) { return objectName.IndexOf("Cloaked", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } } }