using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ETGGUI; using Gunfiguration; using Gungeon; using MonoMod.RuntimeDetour; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using SGUI; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("ItemTipsMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3c88a2fb9be593e23ea5a411f65742f18f1e0f11")] [assembly: AssemblyProduct("ItemTipsMod")] [assembly: AssemblyTitle("ItemTipsMod")] [assembly: InternalsVisibleTo("ModTests")] [assembly: AssemblyVersion("1.0.0.0")] namespace ItemTipsMod; public class AdvancedSynergyCache { public AdvancedSynergyEntry[] SourceEntries; public Dictionary SynergyKeyIndex; public Dictionary> ItemIndex; public AdvancedSynergyCache() : this(ArrayHelper.Empty) { } public AdvancedSynergyCache(AdvancedSynergyEntry[] sourceEntries) { SourceEntries = sourceEntries; Build(); } private void Build() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 SynergyKeyIndex = new Dictionary(); ItemIndex = new Dictionary>(); AdvancedSynergyEntry[] sourceEntries = SourceEntries; foreach (AdvancedSynergyEntry val in sourceEntries) { if (!string.IsNullOrEmpty(val.NameKey) && (int)val.ActivationStatus != 1 && (int)val.ActivationStatus != 2) { if (SynergyKeyIndex.TryGetValue(val.NameKey, out var value)) { value.Entries.Add(val); continue; } SynergyKeyIndex[val.NameKey] = new SynergyGroup(val.NameKey) { Entries = { val } }; } } foreach (KeyValuePair item in SynergyKeyIndex) { _ = item.Key; SynergyGroup value2 = item.Value; foreach (AdvancedSynergyEntry entry in value2.Entries) { AddSynergyEntries(entry.MandatoryGunIDs, value2); AddSynergyEntries(entry.MandatoryItemIDs, value2); AddSynergyEntries(entry.OptionalGunIDs, value2); AddSynergyEntries(entry.OptionalItemIDs, value2); } } } private void AddSynergyEntries(List ids, SynergyGroup group) { if (ids == null) { return; } foreach (int id in ids) { if (ItemIndex.TryGetValue(id, out var value)) { if (!value.Contains(group)) { value.Add(group); } } else { ItemIndex[id] = new List { group }; } } } } public class SynergyGroup { public readonly string NameKey; public List Entries = new List(); public SynergyGroup(string nameKey) { NameKey = nameKey; } public bool SynergyIsAvailable(PlayerController p1, PlayerController p2, int additionalID = -1) { foreach (AdvancedSynergyEntry entry in Entries) { if (entry.SynergyIsAvailable(p1, p2, additionalID)) { return true; } } return false; } } public sealed class AmmonomiconEntryCache { private readonly PickupAndSynergyDataCache _dataCache; private readonly AdvancedSynergyCache _synergyCache; private GungeonSupportedLanguages _currentLanguage; private Dictionary _entryCache; public AmmonomiconEntryCache(AdvancedSynergyCache synergyCache, PickupAndSynergyDataCache dataCache) { _synergyCache = synergyCache; _dataCache = dataCache; _entryCache = new Dictionary(); } public GungeonSupportedLanguages GetCacheLanguage() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _currentLanguage; } public void SetCacheLanguage(GungeonSupportedLanguages value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (value != _currentLanguage) { _entryCache = new Dictionary(); _currentLanguage = value; } } public string GetEntry(int pickupId, string origEntry) { if (pickupId < 0) { return origEntry; } if (_entryCache.TryGetValue(pickupId, out var value)) { return value; } if (!_dataCache.Pickups.TryGetValue(pickupId, out var value2)) { value2 = null; } PickupObject byId = PickupObjectDatabase.GetById(pickupId); string text = GenerateAmmonomiconText(byId, value2); string text2 = (origEntry + "\n" + text).Trim(); _entryCache[pickupId] = text2; return text2; } private string GenerateAmmonomiconText(PickupObject pickup, ItemData item) { StringBuilder builder = new StringBuilder(); builder.AppendLine("----------"); TipBuilder.AppendQualityDescriptor(pickup, delegate(string line) { builder.AppendLine(line); }); TipBuilder.AppendActiveCooldownType(pickup, delegate(string line) { builder.AppendLine(line); }); if (item != null) { builder.AppendLine(item.Notes); } TipBuilder.AppendModifiers(pickup, delegate(StatType stat, string line) { builder.AppendLine(line); }); HashSet hashSet = new HashSet(); if (_synergyCache.ItemIndex.TryGetValue(pickup.PickupObjectId, out var value) && value.Count > 0) { builder.AppendLine("Synergies:"); foreach (SynergyGroup item2 in value) { string synergyString = StringTableManager.GetSynergyString(item2.NameKey, -1); if (hashSet.Add(synergyString)) { string text = (item2.SynergyIsAvailable(GameManager.Instance.PrimaryPlayer, GameManager.Instance.SecondaryPlayer) ? "*" : ""); if (_dataCache.Synergies.TryGetValue(item2.NameKey, out var value2)) { builder.AppendLine(" - " + text + synergyString + text + ": " + value2.Effect); } else { builder.AppendLine(" - " + text + synergyString + text); } } } } if (item != null && item.Synergies.Length != 0) { if (hashSet.Count == 0) { builder.AppendLine("Synergies:"); } SynergyData[] synergies = item.Synergies; foreach (SynergyData synergyData in synergies) { string localizedSynergyName = synergyData.GetLocalizedSynergyName(); if (hashSet.Add(localizedSynergyName)) { string effect = synergyData.Effect; string text2 = (synergyData.InternalWouldGetSynergy() ? "*" : ""); if (!string.IsNullOrEmpty(effect)) { builder.AppendLine(" - " + text2 + localizedSynergyName + text2 + ": " + effect); } else { builder.AppendLine(" - " + text2 + localizedSynergyName + text2); } } } } return builder.ToString(); } } internal static class ArrayHelper { public static T[] Empty { get; } = new T[0]; } internal class CooldownLabelBehavior : MonoBehaviour { public static CooldownLabelBehavior Instance; public PlayerCooldownLabelCollection labelCollection; public bool forceUpdate; public bool forceInitialize; public void UpdateLabelPostion() { PlayerCooldownLabelData[] cooldownLabelData = labelCollection.cooldownLabelData; for (int i = 0; i < cooldownLabelData.Length; i++) { cooldownLabelData[i].UpdateLabelPostion(); } } public void SetVisible(bool visible) { labelCollection.ForEach(delegate(PlayerCooldownLabelData c) { ((dfControl)c.cooldownLabel).IsVisible = visible; }); } private void Start() { try { labelCollection = new PlayerCooldownLabelCollection(); GameManager.Instance.OnNewLevelFullyLoaded += OnNewLevelFullyLoaded; Instance = this; } catch (Exception arg) { StaticLogger.LogDebug("Error in CooldownLabelBehavior.Start: {0}", arg); } } private void OnNewLevelFullyLoaded() { try { labelCollection.Clear(); forceInitialize = true; forceUpdate = true; } catch (Exception arg) { StaticLogger.LogDebug("Error in CooldownLabelBehavior.OnNewLevelFullyLoaded: {0}", arg); } } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 try { if ((int)GameManager.Instance.CurrentLevelOverrideState == 1) { return; } if (GameManager.Instance.IsLoadingLevel) { forceUpdate = true; forceInitialize = true; return; } if (!GameUIRoot.Instance.IsCoreUIVisible()) { forceUpdate = true; return; } if (forceInitialize) { labelCollection.Initialize(GameManager.Instance.AllPlayers.Length); forceInitialize = false; } for (int i = 0; i < GameManager.Instance.AllPlayers.Length; i++) { try { PlayerController player = GameManager.Instance.AllPlayers[i]; PlayerCooldownLabelData playerCooldownLabelData = labelCollection[i]; if (playerCooldownLabelData.UpdateBoundState(player) | forceUpdate) { forceUpdate = false; playerCooldownLabelData.UpdateLabelState(); } } catch (Exception ex) { StaticLogger.LogDebug("Error updating cooldown label for player {0}: {1}", i, ex); } } } catch (Exception arg) { StaticLogger.LogDebug("Error in Cooldown behavior Update: {0}", arg); } } } internal class PlayerCooldownLabelCollection { private static Func GetPlayerItemController = GetPlayerItemControllerDelegate(); public PlayerCooldownLabelData[] cooldownLabelData; public PlayerCooldownLabelData this[int index] => cooldownLabelData[index]; public void ForEach(Action action) { if (cooldownLabelData != null) { PlayerCooldownLabelData[] array = cooldownLabelData; foreach (PlayerCooldownLabelData obj in array) { action(obj); } } } public void Initialize(int playerCount) { StaticLogger.LogDebug("Initializing PlayerCooldownLabelCollection, playerCount: {0}", playerCount); cooldownLabelData = new PlayerCooldownLabelData[playerCount]; for (int i = 0; i < playerCount; i++) { PlayerCooldownLabelData playerCooldownLabelData = new PlayerCooldownLabelData { itemController = GetPlayerItemController?.Invoke(GameUIRoot.Instance, i) }; dfLabel val = ((dfControl)((Component)playerCooldownLabelData.itemController).GetComponent()).AddControl(); val.TextScale = 2f; val.AutoSize = true; ((dfControl)val).IsVisible = false; playerCooldownLabelData.cooldownLabel = val; if (playerCooldownLabelData.itemController.IsRightAligned) { playerCooldownLabelData.BaseRelativeX = 525; playerCooldownLabelData.BaseRelativeX = 510; } else { playerCooldownLabelData.BaseRelativeX = 150; playerCooldownLabelData.BaseRelativeY = 510; } playerCooldownLabelData.UpdateLabelPostion(); cooldownLabelData[i] = playerCooldownLabelData; } } public void Clear() { if (cooldownLabelData != null) { PlayerCooldownLabelData[] array = cooldownLabelData; for (int i = 0; i < array.Length; i++) { array[i].Destroy(); } cooldownLabelData = null; } } private static Func GetPlayerItemControllerDelegate() { MethodInfo method = typeof(GameUIRoot).GetMethod("GetItemControllerForPlayerID", BindingFlags.Instance | BindingFlags.NonPublic); if ((object)method != null) { Delegate @delegate = Delegate.CreateDelegate(typeof(Func), method); Func f = (Func)@delegate; return (GameUIRoot r, int i) => (r?.itemControllers == null) ? null : f(r, i); } StaticLogger.LogDebug("Could not find GetItemControllerForPlayerID"); return (GameUIRoot r, int i) => null; } } internal class PlayerCooldownLabelData { public GameUIItemController itemController; public dfLabel cooldownLabel; public PlayerItem lastItem; public bool isGhost; public int BaseRelativeX; public int BaseRelativeY; public int AdditionalOffsetPx = 2; public dfAnchorStyle Anchor = (dfAnchorStyle)6; public dfPivotPoint Pivot = (dfPivotPoint)6; public int lastActiveCount = -1; public float lastCooldown; public bool showDecimal; public bool UpdateBoundState(PlayerController player) { bool result = false; if (player.IsGhost != isGhost) { isGhost = player.IsGhost; result = true; } if ((Object)(object)player.CurrentItem != (Object)(object)lastItem) { lastItem = player.CurrentItem; result = true; } if (Object.op_Implicit((Object)(object)lastItem)) { showDecimal = false; float num; if (lastItem.CurrentRoomCooldown > 0) { num = lastItem.CurrentRoomCooldown; } else if (lastItem.CurrentDamageCooldown > 0f) { num = (int)lastItem.CurrentDamageCooldown; } else if (lastItem.CurrentTimeCooldown > 0f) { showDecimal = true; num = lastItem.CurrentTimeCooldown; } else { num = 0f; } if (num != lastCooldown) { lastCooldown = num; result = true; } } if (lastActiveCount != player.activeItems.Count) { lastActiveCount = player.activeItems.Count; result = true; } return result; } public void UpdateLabelState() { if (!Object.op_Implicit((Object)(object)lastItem) || lastCooldown == 0f || isGhost) { ((dfControl)cooldownLabel).IsVisible = false; return; } ((dfControl)cooldownLabel).IsVisible = true; string text = (showDecimal ? "0.0" : "0"); cooldownLabel.Text = lastCooldown.ToString(text); UpdateLabelPostion(); } public void UpdateLabelPostion() { //IL_0007: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) ((dfControl)cooldownLabel).Anchor = Anchor; ((dfControl)cooldownLabel).Pivot = Pivot; ((dfControl)cooldownLabel).RelativePosition = new Vector3((float)BaseRelativeX, (float)BaseRelativeY); float num = (float)AdditionalOffsetPx * Pixelator.Instance.CurrentTileScale * (float)itemController.AdditionalItemBoxSprites.Count; if (itemController.IsRightAligned) { num = 0f - num; } ((dfControl)cooldownLabel).RelativePosition = new Vector3((float)BaseRelativeX + num, (float)BaseRelativeY); } public void Destroy() { if (Object.op_Implicit((Object)(object)cooldownLabel)) { Object.Destroy((Object)(object)cooldownLabel); } } } internal static class DebugHelper { internal struct ElapsedHelper : IDisposable { public readonly string Label; public readonly long StartTimestamp; public ElapsedHelper(string label, long startTimestamp) { Label = label; StartTimestamp = startTimestamp; } public void Dispose() { float num = Stopwatch.GetTimestamp() - StartTimestamp; if (num <= 0f) { StaticLogger.LogDebug(Label + " Elapsed: 0 ms"); return; } float num2 = num / (float)Stopwatch.Frequency * 1000f; StaticLogger.LogDebug($"{Label} Elapsed: {num2} ms"); } } public static void AddDebugCommands(ItemTipsModule module) { } public static ElapsedHelper LogElapsed(string label) { return new ElapsedHelper(label, Stopwatch.GetTimestamp()); } } public class ExternalDataCache { public Dictionary Pickups = new Dictionary(); public Dictionary Synergies = new Dictionary(); } public class ExternalDataSet { public ExternalMetadata Metadata; public Dictionary Items; public Dictionary Synergies; } public class ExternalPickupData { public string Name; public string Notes; [JsonIgnore] public ExternalMetadata SourceMetadata; } public class ExternalSynergyData { public string Name; public string Notes; [JsonIgnore] public ExternalMetadata SourceMetadata; } public class ExternalMetadata { public string Name; public string Version; public string Url; } internal static class HookHelper { public static Hook CreateHook(Type sourceType, string sourceMethodName, object newReceiver, string hookMethodName) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown MethodInfo? method = sourceType.GetMethod(sourceMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Delegate @delegate = ReflectionHelper.CreateInstanceDelegate(newReceiver, hookMethodName); return new Hook((MethodBase)method, @delegate); } } public class InternalDataSet { public ItemData[] Items = ArrayHelper.Empty; public SynergyData[] Synergies = ArrayHelper.Empty; } public class SynergyData { public string Key; public PartsList OtherParts; public string Name; public string Effect; public SourceMetadata SourceMetadata; public bool InternalWouldGetSynergy() { if (OtherParts?.Parts == null) { return false; } bool result = false; PlayerController[] allPlayers = GameManager.Instance.AllPlayers; foreach (PlayerController val in allPlayers) { int num = 0; int[] parts = OtherParts.Parts; for (int j = 0; j < parts.Length; j++) { PickupObject byId = PickupObjectDatabase.GetById(parts[j]); if (byId != null && val.HasPickupID(byId.PickupObjectId)) { num++; } if (OtherParts.PartOperand == PartOperand.One && num > 0) { break; } } if (OtherParts.PartOperand == PartOperand.One || OtherParts.PartOperand == PartOperand.OneOrMore) { if (num > 0) { result = true; } } else if (OtherParts.PartOperand == PartOperand.All && num == OtherParts.Parts.Length) { result = true; } } return result; } } public class PartsList { private PartOperand? _operand; public string Operand; public int[] Parts; public PartOperand PartOperand { get { if (!_operand.HasValue) { if (string.IsNullOrEmpty(Operand)) { _operand = PartOperand.Unknown; } else if (string.Equals(Operand, "one+", StringComparison.OrdinalIgnoreCase)) { _operand = PartOperand.OneOrMore; } else { try { _operand = (PartOperand)Enum.Parse(typeof(PartOperand), Operand, ignoreCase: true); } catch (Exception) { _operand = PartOperand.Unknown; } } } return _operand.GetValueOrDefault(); } } } public enum PartOperand { Unknown, One, OneOrMore, All } public class ItemData { public string Name; public int? Id; public int[] Ids; public string Notes; public SynergyData[] Synergies = ArrayHelper.Empty; public SourceMetadata SourceMetadata; } public class SourceMetadata { public string Name; public string Url; public string Version; public static readonly SourceMetadata InternalSource = new SourceMetadata { Name = "internal" }; public override string ToString() { return "SourceMetadata:" + Name; } } internal static class ItemTipsConfig { public static class ConfigKeys { public static readonly string HorizontalPosition = "HorizontalPosition"; public static readonly string VerticalPosition = "VerticalPosition"; public static readonly string TipsEnabled = "TipsEnabled"; public static readonly string AmmonomiconEnabled = "AmmonomiconEnabled"; } internal static Gunfig _Gunfig = null; public static readonly float DefaultHorizontalPosition = 0.01f; public static readonly float DefaultVerticalPosition = 0.2f; public static readonly string DefaultItem = "Default"; public static bool TipsEnabled = true; public static bool AmmonomiconEnabled = true; public static float HorizontalPosition = DefaultHorizontalPosition; public static float VerticalPosition = DefaultVerticalPosition; internal static void Init() { _Gunfig = Gunfig.Get("ItemTips"); CreateItemTipsMenu(_Gunfig); TipsEnabled = _Gunfig.Enabled(ConfigKeys.TipsEnabled); AmmonomiconEnabled = _Gunfig.Enabled(ConfigKeys.AmmonomiconEnabled); HorizontalPosition = TryGetFloatOrDefault(_Gunfig, ConfigKeys.HorizontalPosition, DefaultHorizontalPosition); VerticalPosition = TryGetFloatOrDefault(_Gunfig, ConfigKeys.VerticalPosition, DefaultVerticalPosition); StaticLogger.LogDebug($"{ConfigKeys.TipsEnabled}={TipsEnabled}"); StaticLogger.LogDebug($"{ConfigKeys.AmmonomiconEnabled}={AmmonomiconEnabled}"); StaticLogger.LogDebug($"{ConfigKeys.HorizontalPosition}={HorizontalPosition}"); StaticLogger.LogDebug($"{ConfigKeys.VerticalPosition}={VerticalPosition}"); } private static void CreateItemTipsMenu(Gunfig gunfig) { gunfig.AddToggle(ConfigKeys.TipsEnabled, true, "Tips", (Action)delegate(string key, string value) { TipsEnabled = value == "1"; StaticLogger.LogDebug($"Config Update {ConfigKeys.TipsEnabled}={TipsEnabled}"); }, (Update)1); gunfig.AddToggle(ConfigKeys.AmmonomiconEnabled, true, "Extended Ammonomicon", (Action)delegate(string key, string value) { AmmonomiconEnabled = value == "1"; StaticLogger.LogDebug($"Config Update {ConfigKeys.AmmonomiconEnabled}={AmmonomiconEnabled}"); }, (Update)1); List list = new List { DefaultItem }; list.AddRange(from i in Enumerable.Range(0, 101) select ((float)i / 100f).ToString("0.00")); gunfig.AddScrollBox(ConfigKeys.HorizontalPosition, list, "Horizontal Position", (Action)UpdateFloatCallback, (List)null, (Update)1); gunfig.AddScrollBox(ConfigKeys.VerticalPosition, list, "Vertical Position", (Action)UpdateFloatCallback, (List)null, (Update)1); } private static void UpdateFloatCallback(string key, string newValue) { if (key == ConfigKeys.HorizontalPosition) { float result; if (newValue == DefaultItem) { HorizontalPosition = DefaultHorizontalPosition; } else if (float.TryParse(newValue, out result)) { HorizontalPosition = result; } StaticLogger.LogDebug($"Config Update {ConfigKeys.HorizontalPosition}={HorizontalPosition}"); } else if (key == ConfigKeys.VerticalPosition) { if (newValue == DefaultItem) { VerticalPosition = DefaultVerticalPosition; } if (float.TryParse(newValue, out var result2)) { VerticalPosition = result2; } StaticLogger.LogDebug($"Config Update {ConfigKeys.VerticalPosition}={VerticalPosition}"); } } private static float TryGetFloatOrDefault(Gunfig gunfig, string key, float defaultValue) { string text = gunfig.Value(key); if (text == null) { return defaultValue; } if (text == DefaultItem) { return defaultValue; } if (float.TryParse(text, out var result)) { return result; } return defaultValue; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("glorfindel.etg.itemtips", "ItemTips", "2.2.0")] public class ItemTipsModule : BaseUnityPlugin { public const string GUID = "glorfindel.etg.itemtips"; public const string NAME = "ItemTips"; public const string VERSION = "2.2.0"; private const string DefaultSynergyHighlightColor = "32C23A"; private const string DefaultCoolnessHighlightColor = "7ACAD6"; private const string DefaultCurseHighlightColor = "745AC7"; private static readonly Color LabelBackgroundColor = new Color(0f, 0f, 0f, 0.5f); public ConfigEntry ExternalDataPathConfig; private bool _lateStartCalled; private Settings _currentSettings; private Font _gameFont; private Label _infoLabel; private NearItemOfInterestModifier _modifier; private HashSet _specialItemIds; private PickupAndSynergyDataCache _tipCache; private AmmonomiconEntryCache _ammonomiconCache; private AdvancedSynergyCache _synergyCache; private TipSource? _lastTipSource; private PlayerControllerObserver _observer; public ItemTipsModule() { _tipCache = new PickupAndSynergyDataCache(); _currentSettings = new Settings(); _synergyCache = new AdvancedSynergyCache(); _ammonomiconCache = new AmmonomiconEntryCache(_synergyCache, _tipCache); _specialItemIds = new HashSet { 224, 120, 73, 85, 78, 600, 67 }; } public PickupAndSynergyDataCache GetDataCache() { return _tipCache; } public AdvancedSynergyCache GetSynergyCache() { return _synergyCache; } public void CheckForSynergyManagerChanges() { if (_synergyCache.SourceEntries != GameManager.Instance.SynergyManager.synergies) { StaticLogger.LogDebug("Detected changed source synergies. Reloading..."); _synergyCache = new AdvancedSynergyCache(GameManager.Instance.SynergyManager.synergies); _ammonomiconCache = new AmmonomiconEntryCache(_synergyCache, _tipCache); } } public void Awake() { ExternalDataPathConfig = ((BaseUnityPlugin)this).Config.Bind("Data", "External Data Path", "", "Optional path to directory with external data files. Separate multiple directories with ';'."); } public void Start() { ETGModMainBehaviour.WaitForGameManagerStart((Action)GmStart); } public void GmStart(GameManager gm) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) try { StaticLogger.Source = ((BaseUnityPlugin)this).Logger; StaticLogger.LogDebug("Loading Config"); ItemTipsConfig.Init(); StaticLogger.LogDebug("Initializing UI"); _infoLabel = new Label(); _modifier = new NearItemOfInterestModifier(); SGUIRoot.Main.Children.Add((SElement)(object)_infoLabel); ((SElement)_infoLabel).Background = LabelBackgroundColor; ((SElement)_infoLabel).OnUpdateStyle = delegate(SElement elem) { elem.Font = LoadFont(); ((Label)(object)elem).Reposition(ItemTipsConfig.HorizontalPosition, ItemTipsConfig.VerticalPosition); }; ((SElement)_infoLabel).With.Add((SModifier)(object)_modifier); StaticLogger.LogDebug("Loading internal data"); Stopwatch stopwatch = Stopwatch.StartNew(); _tipCache = Loader.LoadItemData(); _synergyCache = new AdvancedSynergyCache(GameManager.Instance.SynergyManager.synergies); _ammonomiconCache = new AmmonomiconEntryCache(_synergyCache, _tipCache); stopwatch.Stop(); StaticLogger.LogDebug($"Loaded {_tipCache.Pickups.Count} items in {stopwatch.ElapsedMilliseconds} ms"); SetupHoooks(); _observer = ((Component)ETGModMainBehaviour.Instance).gameObject.AddComponent(); PlayerControllerObserver observer = _observer; observer.EnteredCombat = (Action)Delegate.Combine(observer.EnteredCombat, new Action(OnEnteredCombat)); ((Component)ETGModMainBehaviour.Instance).gameObject.AddComponent().ItemTips = this; ((Component)ETGModMainBehaviour.Instance).gameObject.AddComponent(); StaticLogger.LogDebug("Defining commands"); ETGModConsole.Commands.AddGroup("itemtips", (Action)delegate { HelpCommand(ArrayHelper.Empty); }); ConsoleCommandGroup group = ETGModConsole.Commands.GetGroup("itemtips"); group.AddUnit("help", (Action)HelpCommand); group.AddUnit("reload", (Action)ReloadCommand); group.AddUnit("generate", (Action)GenerateSampleCommand); DebugHelper.AddDebugCommands(this); ETGModConsole.Log((object)"ItemTips v2.2.0 loaded", false); } catch (Exception arg) { StaticLogger.LogError(string.Format("ItemTips v{0} Error: {1}", "2.2.0", arg)); ETGModConsole.Log((object)string.Format("ItemTips v{0} Error: {1}", "2.2.0", arg), false); } } private void SetupHoooks() { StaticLogger.LogDebug("Generating hooks"); HookHelper.CreateHook(typeof(Gun), "OnEnteredRange", this, "GunOnEnteredRangeHook"); HookHelper.CreateHook(typeof(Gun), "OnExitRange", this, "GunOnExitRangeHook"); HookHelper.CreateHook(typeof(Gun), "Pickup", this, "GunPickupHook"); HookHelper.CreateHook(typeof(PassiveItem), "OnEnteredRange", this, "PassiveItemOnEnteredRangeHook"); HookHelper.CreateHook(typeof(PassiveItem), "OnExitRange", this, "PassiveItemOnExitRangeHook"); HookHelper.CreateHook(typeof(PassiveItem), "Pickup", this, "PassiveItemPickupHook"); HookHelper.CreateHook(typeof(GungeonMapItem), "Pickup", this, "PassiveItemPickupHook"); HookHelper.CreateHook(typeof(PlayerItem), "OnEnteredRange", this, "ActiveItemOnEnteredRangeHook"); HookHelper.CreateHook(typeof(PlayerItem), "OnExitRange", this, "ActiveItemOnExitRangeHook"); HookHelper.CreateHook(typeof(PlayerItem), "Pickup", this, "ActiveItemPickupHook"); HookHelper.CreateHook(typeof(ShopItemController), "OnEnteredRange", this, "ShopItemOnEnteredRangeHook"); HookHelper.CreateHook(typeof(ShopItemController), "OnExitRange", this, "ShopItemOnExitRangeHook"); HookHelper.CreateHook(typeof(ShopItemController), "Interact", this, "ShopItemInteractHook"); HookHelper.CreateHook(typeof(RewardPedestal), "OnEnteredRange", this, "RewardPedestalOnEnteredRangeHook"); HookHelper.CreateHook(typeof(RewardPedestal), "OnExitRange", this, "RewardPedestalOnExitRangeHook"); HookHelper.CreateHook(typeof(RewardPedestal), "Interact", this, "RewardPedestalInteractHook"); HookHelper.CreateHook(typeof(Chest), "PredictContents", this, "ChestPredictContentsHook"); HookHelper.CreateHook(typeof(HologramDoer), "HideSprite", this, "HologramDoerHideSpriteHook"); HookHelper.CreateHook(typeof(PlayerController), "OnGunChanged", this, "OnGunChangedHook"); HookHelper.CreateHook(typeof(Foyer), "Awake", this, "LateStartAwakeHook"); HookHelper.CreateHook(typeof(EncounterDatabaseEntry), "GetModifiedLongDescription", this, "GetModifiedLongDescriptionHook"); } private void LateStartAwakeHook(Action orig, Foyer self) { orig(self); if (!_lateStartCalled) { _lateStartCalled = true; try { ScanExternalData(); CheckForSynergyManagerChanges(); } catch (Exception arg) { StaticLogger.LogError($"Error automatically loading external files: {arg}"); } } } private int ScanExternalData() { int num = 0; Stopwatch stopwatch = Stopwatch.StartNew(); int num2 = _tipCache.LoadExternalDirectory(Paths.PluginPath); StaticLogger.LogDebug($"Finished loading external data in BepInEx path . Loaded {num2} items. Elapsed: {stopwatch.ElapsedMilliseconds} ms"); num += num2; string value = ExternalDataPathConfig.Value; if (!string.IsNullOrEmpty(value)) { string[] array = value.Split(new char[1] { ';' }); foreach (string text in array) { StaticLogger.LogInfo("Loading external directory: " + text); Stopwatch stopwatch2 = Stopwatch.StartNew(); int num3 = _tipCache.LoadExternalDirectory(text); StaticLogger.LogDebug($"Finished loading external data in '{text}'. Loaded {num3} items. Elapsed: {stopwatch2.ElapsedMilliseconds} ms"); num += num3; } } return num; } private void HologramDoerHideSpriteHook(Action orig, HologramDoer self, GameObject gameObject, bool instant) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, gameObject, instant); } private void OnEnteredCombat() { HideTip(); } private string GetModifiedLongDescriptionHook(Func orig, EncounterDatabaseEntry self) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) string text = orig(self); if (ItemTipsConfig.AmmonomiconEnabled) { try { _ammonomiconCache.SetCacheLanguage(GameManager.Options.CurrentLanguage); int pickupObjectId = self.pickupObjectId; return _ammonomiconCache.GetEntry(pickupObjectId, text); } catch (Exception arg) { StaticLogger.LogError(string.Format("Error in {0}: {1}", "GetModifiedLongDescriptionHook", arg)); } } return text; } private void HelpCommand(string[] args) { if (args.Length != 0) { string text = args[0].ToLowerInvariant(); switch (text) { case "reload": ETGModConsole.Log((object)"Reloads external item description files", false); break; case "generate": ETGModConsole.Log((object)"Generates a sample external item description file", false); break; case "help": ETGModConsole.Log((object)"Help generates help", false); break; default: ETGModConsole.Log((object)("Unknown command " + text), false); break; } } else { ETGModConsole.Log((object)"ItemTips v2.2.0 command help", false); ETGModConsole.Log((object)"itemtips reload - Reload external tip files", false); ETGModConsole.Log((object)"itemtips generate - Create sample external tip file", false); ETGModConsole.Log((object)"itemtips help [command] - Additional help about the command", false); } } private void GenerateSampleCommand(string[] args) { char[] separator = new char[1] { ':' }; string text = null; try { HashSet hashSet = new HashSet(args ?? ArrayHelper.Empty); ExternalDataSet externalDataSet = new ExternalDataSet { Metadata = new ExternalMetadata { Name = "", Url = "", Version = "" }, Items = new Dictionary(), Synergies = new Dictionary() }; foreach (KeyValuePair pair in Game.Items.Pairs) { if (string.IsNullOrEmpty(pair.Key) || _specialItemIds.Contains(pair.Value.PickupObjectId)) { continue; } string text2 = pair.Key; bool flag = false; string item; if (text2.StartsWith("gungeon:")) { item = "gungeon"; if (text2.Contains("_dupe") || (text2.Contains("+") && text2 != "gungeon:+1_bullets")) { continue; } text2 = text2.Substring("gungeon:".Length); flag = true; } else { item = text2.Split(separator)[0]; } string text3 = null; if (_tipCache.Pickups.TryGetValue(pair.Value.PickupObjectId, out var value)) { text3 = value.Notes; } else if (flag) { continue; } if (hashSet.Count <= 0 || hashSet.Contains(item)) { externalDataSet.Items[text2] = new ExternalPickupData { Name = pair.Value.EncounterNameOrDisplayName, Notes = (text3 ?? string.Empty) }; } } foreach (KeyValuePair item2 in _synergyCache.SynergyKeyIndex) { string text4 = null; if (_tipCache.Synergies.TryGetValue(item2.Value.NameKey, out var value2)) { text4 = value2.Effect; string synergyString = StringTableManager.GetSynergyString(item2.Key, -1); externalDataSet.Synergies[item2.Key] = new ExternalSynergyData { Name = synergyString, Notes = (text4 ?? string.Empty) }; } } string path = Loader.DataFilePrefix + "-sample" + Loader.DataFileExtension; text = Path.Combine(Environment.GetEnvironmentVariable("HOME") ?? Environment.GetFolderPath(Environment.SpecialFolder.Personal), path); JsonHelper.SerializeToFile(text, externalDataSet); long length = new FileInfo(text).Length; StaticLogger.LogInfo($"Wrote {length} bytes to to {text}"); ETGModConsole.Log((object)("Wrote sample file to " + text), false); } catch (Exception ex) { if (text != null) { StaticLogger.LogError($"Error creating sample file: '{text}' {ex}"); ETGModConsole.Log((object)$"Error creating sample file: '{text}' {ex}", false); } else { StaticLogger.LogError($"Error creating sample file: {ex}"); ETGModConsole.Log((object)$"Error creating sample file: {ex}", false); } } } private void ReloadCommand(string[] args) { int num = ScanExternalData(); ETGModConsole.Log((object)$"ItemTips reloaded {num} items", false); } private Font LoadFont() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_gameFont == (Object)null) { dfFont val = (dfFont)GameUIRoot.Instance.Manager.DefaultFont; _gameFont = FontConverter.GetFontFromdfFont(val, 2); } return _gameFont; } private void GunOnEnteredRangeHook(Action orig, Gun self, PlayerController player) { try { if (ItemTipsConfig.TipsEnabled) { ShowTip(TipSource.Gun, ((PickupObject)self).PickupObjectId); } } catch (Exception arg) { StaticLogger.LogDebug(string.Format("Error in {0}: {1}", "GunOnEnteredRangeHook", arg)); } orig(self, player); } private void GunOnExitRangeHook(Action orig, Gun self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void GunPickupHook(Action orig, Gun self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void PassiveItemOnEnteredRangeHook(Action orig, PassiveItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { ShowTip(TipSource.PassiveItem, ((PickupObject)self).PickupObjectId); } orig(self, player); } private void PassiveItemOnExitRangeHook(Action orig, PassiveItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void PassiveItemPickupHook(Action orig, PassiveItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void ActiveItemOnEnteredRangeHook(Action orig, PlayerItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { ShowTip(TipSource.ActiveItem, ((PickupObject)self).PickupObjectId); } orig(self, player); } private void ActiveItemOnExitRangeHook(Action orig, PlayerItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void ActiveItemPickupHook(Action orig, PlayerItem self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void ShopItemOnEnteredRangeHook(Action orig, ShopItemController self, PlayerController player) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 if (ItemTipsConfig.TipsEnabled && Object.op_Implicit((Object)(object)self.item)) { int pickupId = (((int)self.CurrencyType != 1) ? self.item.PickupObjectId : (PickupObjectDatabase.GetByEncounterName(self.item.EncounterNameOrDisplayName)?.PickupObjectId ?? (-1))); ShowTip(TipSource.ShopItem, pickupId); } orig(self, player); } private void ShopItemOnExitRangeHook(Action orig, ShopItemController self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void ShopItemInteractHook(Action orig, ShopItemController self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void RewardPedestalOnEnteredRangeHook(Action orig, RewardPedestal self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { int pickupId = self.contents?.PickupObjectId ?? (-1); ShowTip(TipSource.RewardPedestal, pickupId); } orig(self, player); } private void RewardPedestalOnExitRangeHook(Action orig, RewardPedestal self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private void RewardPedestalInteractHook(Action orig, RewardPedestal self, PlayerController player) { if (ItemTipsConfig.TipsEnabled) { HideTip(); } orig(self, player); } private List ChestPredictContentsHook(Func> orig, Chest self, PlayerController player) { List list = orig(self, player); if (list.Count > 0 && ItemTipsConfig.TipsEnabled) { ShowTip(TipSource.ChestPredicted, list[0].PickupObjectId); } return list; } private void OnGunChangedHook(Action orig, PlayerController self, Gun previous, Gun current, Gun previousSecondary, Gun currentSecondary, bool newGun) { if (ItemTipsConfig.TipsEnabled && _lastTipSource.GetValueOrDefault() == TipSource.ChestPredicted) { HideTip(); } orig(self, previous, current, previousSecondary, currentSecondary, newGun); } private void ShrineOnEnteredRangeHook(Action orig, AdvancedShrineController self, PlayerController player) { orig(self, player); } private void ShrineOnExitRangeHook(Action orig, AdvancedShrineController self, PlayerController player) { orig(self, player); } private void ShowTip(TipSource source, int pickupId) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (pickupId < 0) { _modifier.IsNearItem = false; _lastTipSource = null; return; } try { if (_specialItemIds.Contains(pickupId)) { _modifier.IsNearItem = false; _lastTipSource = null; return; } PickupObject byId = PickupObjectDatabase.GetById(pickupId); if (!_tipCache.Pickups.TryGetValue(pickupId, out var value)) { value = null; } TextInfo textInfo = GenerateTextInfo(byId, value); ((SLabel)_infoLabel).Text = textInfo.LabelText; ((SElement)_infoLabel).Size = textInfo.LabelSize; _modifier.IsNearItem = true; _lastTipSource = source; _infoLabel.Reposition(ItemTipsConfig.HorizontalPosition, ItemTipsConfig.VerticalPosition); } catch (Exception arg) { StaticLogger.LogDebug($"ShowTip Error {source} '{pickupId}': {arg}"); } } private void HideTip() { _modifier.IsNearItem = false; _lastTipSource = null; } private TextInfo GenerateTextInfo(PickupObject pickup, ItemData item) { //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) List lines = new List { pickup.EncounterNameOrDisplayName }; TipBuilder.AppendQualityDescriptor(pickup, delegate(string line) { lines.Add(line); }); EncounterDatabaseEntry entry = EncounterDatabase.GetEntry(((BraveBehaviour)pickup).encounterTrackable.EncounterGuid); if (entry != null) { string secondTapeDescriptor = entry.GetSecondTapeDescriptor(); if (entry.shootStyleInt >= 0) { lines.Add("Style: " + secondTapeDescriptor); } else if (entry.isPassiveItem || entry.isPlayerItem) { lines.Add("Type: " + secondTapeDescriptor); } } TipBuilder.AppendActiveCooldownType(pickup, delegate(string line) { lines.Add(line); }); if (item != null && !string.IsNullOrEmpty(item.Notes)) { List collection = ConvertStringToFixedWidthLines(item.Notes, _currentSettings.LineWidth); lines.AddRange(collection); } TipBuilder.AppendModifiers(pickup, delegate(StatType stat, string line) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 string text3 = null; if ((int)stat != 4) { if ((int)stat == 14) { text3 = "745AC7"; } } else { text3 = "7ACAD6"; } if (string.IsNullOrEmpty(text3)) { lines.Add(line); } else { lines.Add("" + line + ""); } }); HashSet hashSet = new HashSet(); if (_synergyCache.ItemIndex.TryGetValue(pickup.PickupObjectId, out var value) && value.Count > 0) { lines.Add("Synergies:"); foreach (SynergyGroup item2 in value) { string synergyString = StringTableManager.GetSynergyString(item2.NameKey, -1); if (!hashSet.Add(synergyString)) { continue; } string text = null; bool num = item2.SynergyIsAvailable(GameManager.Instance.PrimaryPlayer, GameManager.Instance.SecondaryPlayer, pickup.PickupObjectId); if (num && _tipCache.Synergies.TryGetValue(item2.NameKey, out var value2)) { text = value2.Effect; } if (num) { if (!string.IsNullOrEmpty(text)) { lines.Add(" - " + synergyString + ":"); List collection2 = ConvertStringToFixedWidthLines(text, _currentSettings.LineWidth); lines.AddRange(collection2); } else { lines.Add(" - " + synergyString + ""); } } else { lines.Add(" - " + synergyString); } } } if (item != null && item.Synergies.Length != 0) { if (hashSet.Count == 0) { lines.Add("Synergies:"); } SynergyData[] synergies = item.Synergies; foreach (SynergyData synergyData in synergies) { string localizedSynergyName = synergyData.GetLocalizedSynergyName(); if (!hashSet.Add(localizedSynergyName) || (synergyData.Key != null && hashSet.Contains(synergyData.Key))) { continue; } string text2 = null; bool num2 = synergyData.InternalWouldGetSynergy(); if (num2) { text2 = synergyData.Effect; } if (num2) { if (!string.IsNullOrEmpty(text2)) { lines.Add(" - " + localizedSynergyName + ":"); List collection3 = ConvertStringToFixedWidthLines(text2, _currentSettings.LineWidth); lines.AddRange(collection3); } else { lines.Add(" - " + localizedSynergyName + ""); } } else { lines.Add(" - " + localizedSynergyName); } } } Vector2 size = _currentSettings.GetSize(lines.Count); return new TextInfo(string.Join("\n", lines.ToArray()), size); } private static List ConvertStringToFixedWidthLines(string text, int lineWidth) { List list = new List(); if (text.Length < lineWidth) { list.Add(text); } else { string[] array = text.Split(); StringBuilder stringBuilder = new StringBuilder(); string[] array2 = array; foreach (string text2 in array2) { if (stringBuilder.Length + text2.Length + 1 >= lineWidth) { list.Add(stringBuilder.ToString()); stringBuilder.Length = 0; stringBuilder.Append(text2); continue; } if (stringBuilder.Length > 0) { stringBuilder.Append(" "); } stringBuilder.Append(text2); } if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); } } return list; } } public enum ItemType { None, Gun, Passive, Active, Synergy } internal static class JsonHelper { private static readonly JsonSerializerSettings DefaultSettings = new JsonSerializerSettings { Formatting = (Formatting)1, ContractResolver = (IContractResolver)new CamelCasePropertyNamesContractResolver() }; private static readonly JsonSerializer Serializer = JsonSerializer.Create(DefaultSettings); public static T DeserializeFromFile(string filePath) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown using StreamReader streamReader = new StreamReader(filePath); JsonTextReader val = new JsonTextReader((TextReader)streamReader); try { return Serializer.Deserialize((JsonReader)(object)val); } finally { ((IDisposable)val)?.Dispose(); } } public static T DeserializeFromStream(Stream inputStream) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown using StreamReader streamReader = new StreamReader(inputStream); JsonTextReader val = new JsonTextReader((TextReader)streamReader); try { return Serializer.Deserialize((JsonReader)(object)val); } finally { ((IDisposable)val)?.Dispose(); } } public static void SerializeToFile(string filePath, object obj) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown using StreamWriter streamWriter = new StreamWriter(filePath, append: false); JsonTextWriter val = new JsonTextWriter((TextWriter)streamWriter); try { Serializer.Serialize((JsonWriter)(object)val, obj); } finally { ((IDisposable)val)?.Dispose(); } } } internal class Label : SLabel { public void Reposition(float leftFraction, float topFraction) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (((SElement)this).Root != null) { ((SElement)this).Position.x = ((SElement)this).Root.Size.x * leftFraction; ((SElement)this).Position.y = ((SElement)this).Root.Size.y * topFraction; } } } public static class Loader { public static bool ThrowIfException = DefaultThrowSetting(); public static string DataFilePrefix { get; } = "itemtips"; public static string DataFileExtension { get; } = ".tip"; internal static string SearchPattern { get; } = DataFilePrefix + "*" + DataFileExtension; public static PickupAndSynergyDataCache LoadItemData() { PickupAndSynergyDataCache pickupAndSynergyDataCache = new PickupAndSynergyDataCache(); try { InternalDataSet internalDataSet = GetInternalDataSet(); ItemData[] items = internalDataSet.Items; foreach (ItemData itemData in items) { itemData.SourceMetadata = SourceMetadata.InternalSource; if (itemData.Id.HasValue) { pickupAndSynergyDataCache.Pickups[itemData.Id.GetValueOrDefault()] = itemData; } else if (itemData.Ids != null) { int[] ids = itemData.Ids; foreach (int key in ids) { pickupAndSynergyDataCache.Pickups[key] = itemData; } } else { StaticLogger.LogDebug("Could not process item '" + itemData.Name + "' because it does not have an id"); } SynergyData[] synergies = internalDataSet.Synergies; foreach (SynergyData synergyData in synergies) { synergyData.SourceMetadata = SourceMetadata.InternalSource; if (synergyData.Key != null && synergyData.Key.StartsWith("#")) { pickupAndSynergyDataCache.Synergies[synergyData.Key] = synergyData; } else { StaticLogger.LogDebug("Could not process item '" + synergyData.Name + "' because it does not have a proper id"); } } } } catch (Exception arg) when (!ThrowIfException) { StaticLogger.LogInfo($"Error processing item data: {arg}"); } return pickupAndSynergyDataCache; } public static ExternalDataCache LoadExternalTips(string directory) { ExternalDataCache externalDataCache = new ExternalDataCache(); try { if (Directory.Exists(directory)) { int num = 0; string[] files = Directory.GetFiles(directory, SearchPattern, SearchOption.AllDirectories); foreach (string text in files) { try { num++; ProcessJsonFile(externalDataCache, text); } catch (Exception arg) { StaticLogger.LogError($"Failed to read external file '{text}': {arg}"); } } StaticLogger.LogDebug($"Files processed: {num}"); } else { StaticLogger.LogDebug("Directory does not exist: " + directory); } } catch (Exception arg2) { StaticLogger.LogInfo($"Failed to read resource directory '{directory}': {arg2}"); } return externalDataCache; } internal static InternalDataSet GetInternalDataSet() { string[] manifestResourceNames = typeof(ItemTipsModule).Assembly.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { if (!text.Contains("data.json")) { continue; } using Stream inputStream = typeof(ItemTipsModule).Assembly.GetManifestResourceStream(text); InternalDataSet internalDataSet = JsonHelper.DeserializeFromStream(inputStream); if (internalDataSet != null && internalDataSet.Items != null) { return internalDataSet; } StaticLogger.LogDebug("Found stream " + text + " but had no data"); } throw new InvalidOperationException("Could not find internal data set"); } private static void ProcessJsonFile(ExternalDataCache cache, string file) { StaticLogger.LogInfo("Reading file: " + file); using FileStream jsonStream = File.OpenRead(file); ProcessJsonFromStream(cache, jsonStream, "file " + file); } private static void ProcessJsonFromStream(ExternalDataCache cache, Stream jsonStream, string descriptor) { try { ExternalDataSet externalDataSet = JsonHelper.DeserializeFromStream(jsonStream); if (externalDataSet.Metadata != null) { StaticLogger.LogDebug("name:" + externalDataSet.Metadata.Name + ", version:" + externalDataSet.Metadata.Version); } Dictionary dictionary = externalDataSet?.Items; if (dictionary != null && dictionary.Count > 0) { foreach (KeyValuePair item in dictionary) { ExternalPickupData value = item.Value; value.SourceMetadata = externalDataSet.Metadata; if (!string.IsNullOrEmpty(value.Notes) && !value.Notes.EndsWith(".")) { value.Notes += "."; } cache.Pickups[item.Key] = item.Value; } } Dictionary dictionary2 = externalDataSet?.Synergies; if (dictionary2 != null && dictionary2.Count > 0) { foreach (KeyValuePair item2 in dictionary2) { ExternalSynergyData value2 = item2.Value; value2.SourceMetadata = externalDataSet.Metadata; if (!string.IsNullOrEmpty(value2.Notes) && !value2.Notes.EndsWith(".")) { value2.Notes += "."; } cache.Synergies[item2.Key] = item2.Value; } } if (cache.Pickups.Count == 0 && cache.Synergies.Count == 0) { StaticLogger.LogDebug("Nothing found to load in " + descriptor); } } catch (Exception arg) { StaticLogger.LogError($"Failed to read json from {descriptor}: {arg}"); } } private static bool DefaultThrowSetting() { return false; } } internal class NearItemOfInterestModifier : SModifier { public bool IsNearItem; public override void Update() { base.Elem.Enabled = ItemTipsConfig.TipsEnabled; if (GameManager.Instance.IsPaused) { base.Elem.Visible = false; } else { base.Elem.Visible = IsNearItem; } } } public class PickupAndSynergyDataCache { public Dictionary Pickups = new Dictionary(); public Dictionary Synergies = new Dictionary(); public int LoadExternalDirectory(string directory) { int num = 0; try { if (string.IsNullOrEmpty(directory)) { StaticLogger.LogDebug("Skipped loading external files because directory was not set."); return 0; } ExternalDataCache externalDataCache = Loader.LoadExternalTips(directory); if (externalDataCache.Pickups.Count == 0 && externalDataCache.Synergies.Count == 0) { return num; } int num2 = 0; foreach (KeyValuePair pickup in externalDataCache.Pickups) { string key = pickup.Key; ExternalPickupData value = pickup.Value; if (string.IsNullOrEmpty(value.Notes)) { StaticLogger.LogDebug("Skipped item: " + key + ", no 'Notes' set."); num2++; continue; } if (!Game.Items.ContainsID(key)) { StaticLogger.LogDebug("Skipped item: " + key + ", game has not loaded it."); num2++; continue; } PickupObject val = Game.Items[key]; if (val.PickupObjectId < 0) { StaticLogger.LogDebug("Skipped item: " + key + ", pickup has not been assigned an id."); num2++; continue; } SourceMetadata sourceMetadata = null; if (Pickups.TryGetValue(val.PickupObjectId, out var value2)) { if (value2.SourceMetadata == SourceMetadata.InternalSource) { sourceMetadata = SourceMetadata.InternalSource; } else if (value.SourceMetadata != null) { sourceMetadata = new SourceMetadata { Name = value.SourceMetadata.Name, Version = value.SourceMetadata.Version, Url = value.SourceMetadata.Url }; } } else { sourceMetadata = new SourceMetadata { Name = value.SourceMetadata.Name, Version = value.SourceMetadata.Version, Url = value.SourceMetadata.Url }; } Pickups[val.PickupObjectId] = new ItemData { Name = (value.Name ?? key), Notes = value.Notes, SourceMetadata = sourceMetadata }; num++; } foreach (KeyValuePair synergy in externalDataCache.Synergies) { string key2 = synergy.Key; ExternalSynergyData value3 = synergy.Value; if (string.IsNullOrEmpty(value3.Notes)) { StaticLogger.LogDebug("Skipped synergy: " + key2 + ", no 'Notes' set."); num2++; continue; } SourceMetadata sourceMetadata2 = null; if (Synergies.TryGetValue(key2, out var value4)) { if (value4.SourceMetadata == SourceMetadata.InternalSource) { sourceMetadata2 = SourceMetadata.InternalSource; } else if (value3.SourceMetadata != null) { sourceMetadata2 = new SourceMetadata { Name = value3.SourceMetadata.Name, Version = value3.SourceMetadata.Version, Url = value3.SourceMetadata.Url }; } } else { sourceMetadata2 = new SourceMetadata { Name = value3.SourceMetadata.Name, Version = value3.SourceMetadata.Version, Url = value3.SourceMetadata.Url }; } Synergies[key2] = new SynergyData { Name = (value3.Name ?? key2), Effect = value3.Notes, SourceMetadata = sourceMetadata2 }; num++; } StaticLogger.LogDebug($"Loaded external tips:{num}, skipped:{num2}"); return num; } catch (Exception arg) { StaticLogger.LogError($"Failed to load external data. Error: {arg}"); return num; } } } public class PlayerControllerObserver : MonoBehaviour { private Func getLastInteractable; public float TimeBetweenChecks = 1f; public Action EnteredCombat; public Action ExitedCombat; public Action LastInteractableChanged; private float TimeCounter; private bool _currentlyInCombat; private IPlayerInteractable _lastInteractable; private void Start() { try { getLastInteractable = ReflectionHelper.CreatePrivateFieldGetter("m_lastInteractionTarget"); } catch (Exception arg) { StaticLogger.LogInfo($"Cannot inspect player interactions: {arg}"); } } private void Update() { TimeCounter -= Time.deltaTime; if (!(TimeCounter <= 0f)) { return; } TimeCounter = TimeBetweenChecks; try { bool flag = false; PlayerController[] allPlayers = GameManager.Instance.AllPlayers; foreach (PlayerController val in allPlayers) { if (val.IsInCombat) { flag = true; break; } IPlayerInteractable val2 = getLastInteractable(val); if (_lastInteractable != val2) { _lastInteractable = val2; LastInteractableChanged?.Invoke(val, _lastInteractable); break; } } if (_currentlyInCombat != flag) { _currentlyInCombat = flag; if (flag) { EnteredCombat?.Invoke(); } else { ExitedCombat?.Invoke(); } } } catch (Exception arg) { StaticLogger.LogDebug($"Error in Update: {arg}"); } } } internal delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); internal delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); internal delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); internal static class ReflectionHelper { private static Type[] ActionDelegateTypes = new Type[8] { typeof(Action), typeof(Action<>), typeof(Action<, >), typeof(Action<, , >), typeof(Action<, , , >), typeof(Action<, , , , >), typeof(Action<, , , , , >), typeof(Action<, , , , , , >) }; private static Type[] FuncDelegateTypes = new Type[5] { typeof(Func<>), typeof(Func<, >), typeof(Func<, , >), typeof(Func<, , , >), typeof(Func<, , , , >) }; public static Func CreatePrivateFieldGetter(string fieldName) { FieldInfo field = typeof(TTarget).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); if ((object)field == null) { throw new ArgumentException($"Private instance field {fieldName} was not found on {typeof(TTarget)}"); } return (TTarget t) => (TField)field.GetValue(t); } public static Delegate CreateInstanceDelegate(object source, string name) { MethodInfo method = source.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((object)method == null) { throw new ArgumentException($"Method '{name}' not found on {source.GetType()}"); } Type[] array = (((object)method.ReturnType != typeof(void)) ? FuncDelegateTypes : ActionDelegateTypes); ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length >= array.Length) { throw new ArgumentException($"Too many parameters for delegate types:{parameters.Length}"); } Type type = array[parameters.Length]; Type type2; if (type.IsGenericType) { List list = new List(parameters.Select((ParameterInfo p) => p.ParameterType)); if ((object)method.ReturnType == typeof(void)) { type2 = type.MakeGenericType(list.ToArray()); } else { list.Add(method.ReturnType); type2 = type.MakeGenericType(list.ToArray()); } } else { type2 = type; } return Delegate.CreateDelegate(type2, source, method); } } public class Settings { public int LineWidth = 40; public Vector2 GetSize(int linesHeight) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector2(11f * (float)LineWidth, 32f * (float)linesHeight); } } internal static class StaticLogger { public static ManualLogSource Source; public static void LogInfo(string message) { ManualLogSource source = Source; if (source != null) { source.LogInfo((object)message); } } public static void LogDebug(string message) { ManualLogSource source = Source; if (source != null) { source.LogDebug((object)message); } } public static void LogDebug(string format, T arg) { ManualLogSource source = Source; if (source != null) { source.LogDebug((object)string.Format(format, arg)); } } public static void LogDebug(string format, params object[] args) { ManualLogSource source = Source; if (source != null) { source.LogDebug((object)string.Format(format, args)); } } public static void LogError(string format, params object[] args) { ManualLogSource source = Source; if (source != null) { source.LogError((object)string.Format(format, args)); } } } internal static class SynergyDataExtensions { public static string GetLocalizedSynergyName(this SynergyData synergy) { if (synergy.Key == null) { return synergy.Name; } string synergyString = StringTableManager.GetSynergyString(synergy.Key, -1); if (string.IsNullOrEmpty(synergyString)) { StaticLogger.LogDebug("Did not get localized name for " + synergy.Key); return synergy.Key; } return synergyString; } } internal class SynergyManagerMonitorBehaviour : MonoBehaviour { public ItemTipsModule ItemTips; private void Update() { if ((Object)(object)ItemTips != (Object)null) { ItemTips.CheckForSynergyManagerChanges(); } } } internal struct TextInfo { public readonly string LabelText; public readonly Vector2 LabelSize; public TextInfo(string text, Vector2 size) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) LabelText = text; LabelSize = size; } } internal static class TipBuilder { public static void AppendQualityDescriptor(PickupObject pickup, Action appendLine) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) if ((int)pickup.quality != 0 && (int)pickup.quality != -100 && (int)pickup.quality != -50) { if (pickup.ItemSpansBaseQualityTiers || pickup.ItemRespectsHeartMagnificence) { appendLine("Quality: C/B/A"); } else { appendLine($"Quality: {pickup.quality}"); } } } public static void AppendActiveCooldownType(PickupObject pickup, Action appendLine) { PlayerItem val = (PlayerItem)(object)((pickup is PlayerItem) ? pickup : null); if (val != null) { if (val.roomCooldown > 0) { appendLine("Cooldown: Room"); } else if (val.damageCooldown > 0f) { appendLine("Cooldown: Damage"); } else if (val.timeCooldown > 0f) { appendLine("Cooldown: Time"); } } } public static void AppendModifiers(PickupObject pickup, Action appendLine) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Invalid comparison between Unknown and I4 //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Invalid comparison between Unknown and I4 List list = new List(); Gun val = (Gun)(object)((pickup is Gun) ? pickup : null); if (val == null) { BasicStatPickup val2 = (BasicStatPickup)(object)((pickup is BasicStatPickup) ? pickup : null); if (val2 == null) { PassiveItem val3 = (PassiveItem)(object)((pickup is PassiveItem) ? pickup : null); if (val3 == null) { PlayerItem val4 = (PlayerItem)(object)((pickup is PlayerItem) ? pickup : null); if (val4 == null) { StaticLogger.LogDebug("Could not get stat modifiers: " + pickup.EncounterNameOrDisplayName); return; } if (val4.passiveStatModifiers != null) { list.AddRange(val4.passiveStatModifiers); } } else if (val3.passiveStatModifiers != null) { list.AddRange(val3.passiveStatModifiers); } } else { if (((PassiveItem)val2).passiveStatModifiers != null) { list.AddRange(((PassiveItem)val2).passiveStatModifiers); } if (val2.modifiers != null) { list.AddRange(val2.modifiers); } } } else if (val.passiveStatModifiers != null) { list.AddRange(val.passiveStatModifiers); } if (list.Count <= 0) { return; } float num = 0f; float num2 = 0f; foreach (StatModifier item in list) { if ((int)item.statToBoost == 14) { num += item.amount; } else if ((int)item.statToBoost == 4) { num2 += item.amount; } } if (num > 0f) { appendLine((StatType)14, $"Curse: +{num}"); } if (num2 > 0f) { appendLine((StatType)4, $"Coolness: +{num2}"); } } } public enum TipSource { Gun, ActiveItem, PassiveItem, ShopItem, RewardPedestal, ChestPredicted }