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<string, SynergyGroup> SynergyKeyIndex;
public Dictionary<int, List<SynergyGroup>> ItemIndex;
public AdvancedSynergyCache()
: this(ArrayHelper<AdvancedSynergyEntry>.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<string, SynergyGroup>();
ItemIndex = new Dictionary<int, List<SynergyGroup>>();
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<string, SynergyGroup> 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<int> 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<SynergyGroup> { group };
}
}
}
}
public class SynergyGroup
{
public readonly string NameKey;
public List<AdvancedSynergyEntry> Entries = new List<AdvancedSynergyEntry>();
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<int, string> _entryCache;
public AmmonomiconEntryCache(AdvancedSynergyCache synergyCache, PickupAndSynergyDataCache dataCache)
{
_synergyCache = synergyCache;
_dataCache = dataCache;
_entryCache = new Dictionary<int, string>();
}
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<int, string>();
_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<string> hashSet = new HashSet<string>();
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<T>
{
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<GameUIRoot, int, GameUIItemController> GetPlayerItemController = GetPlayerItemControllerDelegate();
public PlayerCooldownLabelData[] cooldownLabelData;
public PlayerCooldownLabelData this[int index] => cooldownLabelData[index];
public void ForEach(Action<PlayerCooldownLabelData> 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<dfPanel>()).AddControl<dfLabel>();
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<GameUIRoot, int, GameUIItemController> GetPlayerItemControllerDelegate()
{
MethodInfo method = typeof(GameUIRoot).GetMethod("GetItemControllerForPlayerID", BindingFlags.Instance | BindingFlags.NonPublic);
if ((object)method != null)
{
Delegate @delegate = Delegate.CreateDelegate(typeof(Func<GameUIRoot, int, GameUIItemController>), method);
Func<GameUIRoot, int, GameUIItemController> f = (Func<GameUIRoot, int, GameUIItemController>)@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<string, ExternalPickupData> Pickups = new Dictionary<string, ExternalPickupData>();
public Dictionary<string, ExternalSynergyData> Synergies = new Dictionary<string, ExternalSynergyData>();
}
public class ExternalDataSet
{
public ExternalMetadata Metadata;
public Dictionary<string, ExternalPickupData> Items;
public Dictionary<string, ExternalSynergyData> 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<ItemData>.Empty;
public SynergyData[] Synergies = ArrayHelper<SynergyData>.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<SynergyData>.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<string, string>)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<string, string>)delegate(string key, string value)
{
AmmonomiconEnabled = value == "1";
StaticLogger.LogDebug($"Config Update {ConfigKeys.AmmonomiconEnabled}={AmmonomiconEnabled}");
}, (Update)1);
List<string> list = new List<string> { 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<string, string>)UpdateFloatCallback, (List<string>)null, (Update)1);
gunfig.AddScrollBox(ConfigKeys.VerticalPosition, list, "Vertical Position", (Action<string, string>)UpdateFloatCallback, (List<string>)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<string> ExternalDataPathConfig;
private bool _lateStartCalled;
private Settings _currentSettings;
private Font _gameFont;
private Label _infoLabel;
private NearItemOfInterestModifier _modifier;
private HashSet<int> _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<int> { 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<string>("Data", "External Data Path", "", "Optional path to directory with external data files. Separate multiple directories with ';'.");
}
public void Start()
{
ETGModMainBehaviour.WaitForGameManagerStart((Action<GameManager>)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>();
PlayerControllerObserver observer = _observer;
observer.EnteredCombat = (Action)Delegate.Combine(observer.EnteredCombat, new Action(OnEnteredCombat));
((Component)ETGModMainBehaviour.Instance).gameObject.AddComponent<SynergyManagerMonitorBehaviour>().ItemTips = this;
((Component)ETGModMainBehaviour.Instance).gameObject.AddComponent<CooldownLabelBehavior>();
StaticLogger.LogDebug("Defining commands");
ETGModConsole.Commands.AddGroup("itemtips", (Action<string[]>)delegate
{
HelpCommand(ArrayHelper<string>.Empty);
});
ConsoleCommandGroup group = ETGModConsole.Commands.GetGroup("itemtips");
group.AddUnit("help", (Action<string[]>)HelpCommand);
group.AddUnit("reload", (Action<string[]>)ReloadCommand);
group.AddUnit("generate", (Action<string[]>)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<Foyer> 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<HologramDoer, GameObject, bool> orig, HologramDoer self, GameObject gameObject, bool instant)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, gameObject, instant);
}
private void OnEnteredCombat()
{
HideTip();
}
private string GetModifiedLongDescriptionHook(Func<EncounterDatabaseEntry, string> 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<string> hashSet = new HashSet<string>(args ?? ArrayHelper<string>.Empty);
ExternalDataSet externalDataSet = new ExternalDataSet
{
Metadata = new ExternalMetadata
{
Name = "",
Url = "",
Version = ""
},
Items = new Dictionary<string, ExternalPickupData>(),
Synergies = new Dictionary<string, ExternalSynergyData>()
};
foreach (KeyValuePair<string, PickupObject> 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<string, SynergyGroup> 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<Gun, PlayerController> 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<Gun, PlayerController> orig, Gun self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void GunPickupHook(Action<Gun, PlayerController> orig, Gun self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void PassiveItemOnEnteredRangeHook(Action<PassiveItem, PlayerController> orig, PassiveItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
ShowTip(TipSource.PassiveItem, ((PickupObject)self).PickupObjectId);
}
orig(self, player);
}
private void PassiveItemOnExitRangeHook(Action<PassiveItem, PlayerController> orig, PassiveItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void PassiveItemPickupHook(Action<PassiveItem, PlayerController> orig, PassiveItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void ActiveItemOnEnteredRangeHook(Action<PlayerItem, PlayerController> orig, PlayerItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
ShowTip(TipSource.ActiveItem, ((PickupObject)self).PickupObjectId);
}
orig(self, player);
}
private void ActiveItemOnExitRangeHook(Action<PlayerItem, PlayerController> orig, PlayerItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void ActiveItemPickupHook(Action<PlayerItem, PlayerController> orig, PlayerItem self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void ShopItemOnEnteredRangeHook(Action<ShopItemController, PlayerController> 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<ShopItemController, PlayerController> orig, ShopItemController self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void ShopItemInteractHook(Action<ShopItemController, PlayerController> orig, ShopItemController self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void RewardPedestalOnEnteredRangeHook(Action<RewardPedestal, PlayerController> 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<RewardPedestal, PlayerController> orig, RewardPedestal self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private void RewardPedestalInteractHook(Action<RewardPedestal, PlayerController> orig, RewardPedestal self, PlayerController player)
{
if (ItemTipsConfig.TipsEnabled)
{
HideTip();
}
orig(self, player);
}
private List<PickupObject> ChestPredictContentsHook(Func<Chest, PlayerController, List<PickupObject>> orig, Chest self, PlayerController player)
{
List<PickupObject> list = orig(self, player);
if (list.Count > 0 && ItemTipsConfig.TipsEnabled)
{
ShowTip(TipSource.ChestPredicted, list[0].PickupObjectId);
}
return list;
}
private void OnGunChangedHook(Action<PlayerController, Gun, Gun, Gun, Gun, bool> 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<AdvancedShrineController, PlayerController> orig, AdvancedShrineController self, PlayerController player)
{
orig(self, player);
}
private void ShrineOnExitRangeHook(Action<AdvancedShrineController, PlayerController> 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<string> lines = new List<string> { 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<string> 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("<color=#" + text3 + ">" + line + "</color>");
}
});
HashSet<string> hashSet = new HashSet<string>();
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(" - <color=#32C23A>" + synergyString + "</color>:");
List<string> collection2 = ConvertStringToFixedWidthLines(text, _currentSettings.LineWidth);
lines.AddRange(collection2);
}
else
{
lines.Add(" - <color=#32C23A>" + synergyString + "</color>");
}
}
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(" - <color=#32C23A>" + localizedSynergyName + "</color>:");
List<string> collection3 = ConvertStringToFixedWidthLines(text2, _currentSettings.LineWidth);
lines.AddRange(collection3);
}
else
{
lines.Add(" - <color=#32C23A>" + localizedSynergyName + "</color>");
}
}
else
{
lines.Add(" - " + localizedSynergyName);
}
}
}
Vector2 size = _currentSettings.GetSize(lines.Count);
return new TextInfo(string.Join("\n", lines.ToArray()), size);
}
private static List<string> ConvertStringToFixedWidthLines(string text, int lineWidth)
{
List<string> list = new List<string>();
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<T>(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<T>((JsonReader)(object)val);
}
finally
{
((IDisposable)val)?.Dispose();
}
}
public static T DeserializeFromStream<T>(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<T>((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<InternalDataSet>(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<ExternalDataSet>(jsonStream);
if (externalDataSet.Metadata != null)
{
StaticLogger.LogDebug("name:" + externalDataSet.Metadata.Name + ", version:" + externalDataSet.Metadata.Version);
}
Dictionary<string, ExternalPickupData> dictionary = externalDataSet?.Items;
if (dictionary != null && dictionary.Count > 0)
{
foreach (KeyValuePair<string, ExternalPickupData> 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<string, ExternalSynergyData> dictionary2 = externalDataSet?.Synergies;
if (dictionary2 != null && dictionary2.Count > 0)
{
foreach (KeyValuePair<string, ExternalSynergyData> 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<int, ItemData> Pickups = new Dictionary<int, ItemData>();
public Dictionary<string, SynergyData> Synergies = new Dictionary<string, SynergyData>();
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<string, ExternalPickupData> 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<string, ExternalSynergyData> 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<PlayerController, IPlayerInteractable> getLastInteractable;
public float TimeBetweenChecks = 1f;
public Action EnteredCombat;
public Action ExitedCombat;
public Action<PlayerController, IPlayerInteractable> LastInteractableChanged;
private float TimeCounter;
private bool _currentlyInCombat;
private IPlayerInteractable _lastInteractable;
private void Start()
{
try
{
getLastInteractable = ReflectionHelper.CreatePrivateFieldGetter<PlayerController, IPlayerInteractable>("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, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
internal delegate void Action<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7>(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<TTarget, TField> CreatePrivateFieldGetter<TTarget, TField>(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<Type> list = new List<Type>(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<T>(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<string> 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<string> 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<StatType, string> 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<StatModifier> list = new List<StatModifier>();
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
}