Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AnglersEye v0.1.0
plugins/AnglersEye/AnglersEye.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using AnglersEye.Core; using AnglersEye.Core.Model; using AnglersEye.UI; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("AnglersEye")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A fishing assistant: fish ID, smart bait, bite and struggle cues, catch forecast. Client-side.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+c6687ef0c664fefa73a1920d8f374c8a51140e34")] [assembly: AssemblyProduct("AnglersEye")] [assembly: AssemblyTitle("AnglersEye")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? IsAdvanced; public bool? Browsable; public bool? ReadOnly; } namespace AnglersEye { [BepInPlugin("com.jumpingmushroom.anglerseye", "Angler's Eye", "0.1.0")] [BepInProcess("valheim.exe")] [BepInProcess("valheim.x86_64")] public sealed class AnglersEyePlugin : BaseUnityPlugin { public const string PluginGuid = "com.jumpingmushroom.anglerseye"; public const string PluginName = "Angler's Eye"; public const string PluginVersion = "0.1.0"; internal static ManualLogSource Log; internal static AnglersEyePlugin Instance; private static readonly HashSet<string> Warned = new HashSet<string>(); private Harmony _harmony; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; PluginConfig.Bind(((BaseUnityPlugin)this).Config); ConsoleCommands.Register(); _harmony = new Harmony("com.jumpingmushroom.anglerseye"); _harmony.PatchAll(typeof(AnglersEyePlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Angler's Eye 0.1.0 loaded."); } private void Update() { Runtime.Tick(); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } internal static void WarnOnce(string key, Exception e) { if (Warned.Add(key)) { Log.LogWarning((object)(key + ": " + e)); } } } public static class PluginConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> HoverInfo; public static ConfigEntry<bool> FloatLabel; public static ConfigEntry<bool> ShowOdds; public static ConfigEntry<bool> SmartBait; public static ConfigEntry<float> AimConeDegrees; public static ConfigEntry<bool> BiteCue; public static ConfigEntry<bool> BiteSound; public static ConfigEntry<float> BiteVolume; public static ConfigEntry<bool> StruggleIndicator; public static ConfigEntry<bool> Forecast; public static ConfigEntry<bool> SmartReel; public static ConfigEntry<bool> ExtendedHookWindow; public static ConfigEntry<float> HookWindowSeconds; public static ConfigEntry<float> Scale; public static ConfigEntry<float> OffsetX; public static ConfigEntry<float> OffsetY; public static ConfigEntry<bool> Verbose; private static ConfigurationManagerAttributes Attr(int order, bool advanced = false) { return new ConfigurationManagerAttributes { Order = order, IsAdvanced = advanced }; } public static bool Toggle(Feature f) { return f switch { Feature.HoverInfo => HoverInfo.Value, Feature.FloatLabel => FloatLabel.Value, Feature.SmartBait => SmartBait.Value, Feature.BiteCue => BiteCue.Value, Feature.StruggleIndicator => StruggleIndicator.Value, Feature.Forecast => Forecast.Value, Feature.SmartReel => SmartReel.Value, Feature.HookWindow => ExtendedHookWindow.Value, _ => false, }; } public static void Bind(ConfigFile cfg) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Expected O, but got Unknown //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Expected O, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Expected O, but got Unknown //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Expected O, but got Unknown //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Expected O, but got Unknown //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Expected O, but got Unknown //IL_036b: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Expected O, but got Unknown //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Expected O, but got Unknown //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Expected O, but got Unknown Enabled = cfg.Bind<bool>("1 - General", "Enabled", true, new ConfigDescription("Master switch for everything Angler's Eye shows or does.", (AcceptableValueBase)null, new object[1] { Attr(100) })); HoverInfo = cfg.Bind<bool>("2 - Identify", "HoverInfo", true, new ConfigDescription("When you look at a fish, add its stars and the bait it takes to the hover text: how many you carry, or ✖ if none.", (AcceptableValueBase)null, new object[1] { Attr(90) })); FloatLabel = cfg.Bind<bool>("2 - Identify", "FloatLabel", true, new ConfigDescription("Show a small label above your float naming the fish heading for it, and whether your bait works on it.", (AcceptableValueBase)null, new object[1] { Attr(89) })); ShowOdds = cfg.Bind<bool>("2 - Identify", "ShowOdds", false, new ConfigDescription("Also show the bait's chance per nibble, e.g. 'Cold bait 60%'.", (AcceptableValueBase)null, new object[1] { Attr(88) })); SmartBait = cfg.Bind<bool>("3 - Bait", "SmartBait", true, new ConfigDescription("On cast, equip the carried bait that works best on the fish you're aiming at. Tells you if you carry none.", (AcceptableValueBase)null, new object[1] { Attr(80) })); AimConeDegrees = cfg.Bind<float>("3 - Bait", "AimConeDegrees", 10f, new ConfigDescription("How far off your aim (in degrees) a fish can be and still count as the one you're aiming at.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 30f), new object[1] { Attr(79) })); BiteCue = cfg.Bind<bool>("4 - Cues", "BiteCue", true, new ConfigDescription("Show 'BITE!' in the fishing panel while a nibble can be hooked.", (AcceptableValueBase)null, new object[1] { Attr(70) })); BiteSound = cfg.Bind<bool>("4 - Cues", "BiteSound", true, new ConfigDescription("Play a short sound on a nibble you can hook. Needs BiteCue on.", (AcceptableValueBase)null, new object[1] { Attr(69) })); BiteVolume = cfg.Bind<float>("4 - Cues", "BiteVolume", 0.7f, new ConfigDescription("Volume of the bite sound.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1] { Attr(68) })); StruggleIndicator = cfg.Bind<bool>("4 - Cues", "StruggleIndicator", true, new ConfigDescription("While a fish is hooked, show REEL when it's calm and WAIT (with a bar counting down the struggle) when it's struggling.", (AcceptableValueBase)null, new object[1] { Attr(67) })); Forecast = cfg.Bind<bool>("5 - Forecast", "Forecast", true, new ConfigDescription("Estimate whether you have the stamina to land the fish: can land, tight or unlikely.", (AcceptableValueBase)null, new object[1] { Attr(60) })); SmartReel = cfg.Bind<bool>("6 - Assists", "SmartReel", false, new ConfigDescription("While you hold Block, only reel while the fish is calm. Stamina costs stay vanilla. Fishing skill won't rise while it's waiting out a struggle.", (AcceptableValueBase)null, new object[1] { Attr(50) })); ExtendedHookWindow = cfg.Bind<bool>("6 - Assists", "ExtendedHookWindow", false, new ConfigDescription("Give yourself longer than vanilla's 0.5 s to hook a nibble.", (AcceptableValueBase)null, new object[1] { Attr(49) })); HookWindowSeconds = cfg.Bind<float>("6 - Assists", "HookWindowSeconds", 1f, new ConfigDescription("Hook window when ExtendedHookWindow is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1.5f), new object[1] { Attr(48) })); Scale = cfg.Bind<float>("7 - UI", "Scale", 1f, new ConfigDescription("Size of the fishing panel and the float label.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), new object[1] { Attr(40) })); OffsetX = cfg.Bind<float>("7 - UI", "OffsetX", 0f, new ConfigDescription("Horizontal nudge of the fishing panel from its spot above the stamina bar, in pixels (positive is right).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1500f, 1500f), new object[1] { Attr(39) })); OffsetY = cfg.Bind<float>("7 - UI", "OffsetY", 0f, new ConfigDescription("Vertical nudge of the fishing panel from its spot above the stamina bar, in pixels (positive is up).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1000f, 1000f), new object[1] { Attr(38) })); Verbose = cfg.Bind<bool>("8 - Logging", "Verbose", false, new ConfigDescription("Log smart bait and compat decisions to the BepInEx log.", (AcceptableValueBase)null, new object[1] { Attr(5, advanced: true) })); } } } namespace AnglersEye.UI { internal static class BiteCue { private static readonly string[] Candidates = new string[2] { "UI_Craft_Finish_01", "Ui_Click_01" }; private const float RetryLookupAfter = 30f; private static float _until; private static float _nextLookup; private static bool _warnedMissing; private static AudioClip _clip; private static AudioSource _source; public static bool Active => Time.time < _until; public static void Fire() { if (Features.On(Feature.BiteCue)) { _until = Time.time + ReelPolicy.HookWindow(Features.On(Feature.HookWindow), PluginConfig.HookWindowSeconds.Value); if (PluginConfig.BiteSound.Value) { Play(); } } } private static void Play() { if ((Object)(object)_clip == (Object)null && Time.time >= _nextLookup) { _nextLookup = Time.time + 30f; Dictionary<string, AudioClip> dictionary = new Dictionary<string, AudioClip>(); AudioClip[] array = Resources.FindObjectsOfTypeAll<AudioClip>(); foreach (AudioClip val in array) { if ((Object)(object)val != (Object)null && !dictionary.ContainsKey(((Object)val).name)) { dictionary[((Object)val).name] = val; } } string[] candidates = Candidates; foreach (string key in candidates) { if (dictionary.TryGetValue(key, out _clip)) { break; } } if ((Object)(object)_clip == (Object)null && !_warnedMissing) { _warnedMissing = true; AnglersEyePlugin.Log.LogWarning((object)"Angler's Eye: no bite sound found; the visual cue still works."); } } if (!((Object)(object)_clip == (Object)null)) { if ((Object)(object)_source == (Object)null) { _source = ((Component)AnglersEyePlugin.Instance).gameObject.AddComponent<AudioSource>(); _source.playOnAwake = false; _source.spatialBlend = 0f; } if ((Object)(object)AudioMan.instance != (Object)null && (Object)(object)_source.outputAudioMixerGroup == (Object)null) { _source.outputAudioMixerGroup = AudioMan.instance.m_guiMixer; } _source.PlayOneShot(_clip, PluginConfig.BiteVolume.Value); } } } internal static class FishingPanel { private const float Gap = 6f; private const float MinWidth = 170f; private const float TextSize = 20f; private const float BigSize = 30f; private const float StarSize = 18f; private const float BarHeight = 5f; private static readonly int VisibleHash = Animator.StringToHash("Visible"); private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4]; private static readonly List<Image> StarPool = new List<Image>(); private static RectTransform _root; private static RectTransform _titleRow; private static RectTransform _bodyRow; private static RectTransform _bodyCol; private static RectTransform _bar; private static RectTransform _barFill; private static TextMeshProUGUI _title; private static TextMeshProUGUI _body; private static TextMeshProUGUI _forecast; private static Image _barFillImage; private static Hud _builtFor; public static void Show(PanelView v) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) if (v == null || !Ensure()) { Hide(); return; } bool flag = v.Title != null; SetActive(_titleRow, flag); if (flag) { bool starsDrawn = UiUtil.Stars(_titleRow, StarPool, v.Level, 1, 18f); UiUtil.SetText((TMP_Text)(object)_title, UiUtil.NameWithLevel(v.Title, v.Level, starsDrawn)); } bool flag2 = v.Body != null; bool flag3 = v.Forecast != null; SetActive(_bodyRow, flag2 || flag3); SetActive(_bodyCol, flag2); if (flag2) { UiUtil.SetText((TMP_Text)(object)_body, v.Body); ((Graphic)_body).color = Palette.For(v.BodyTone); ((TMP_Text)_body).fontSize = (v.BodyIsBig ? 30f : 20f); ((TMP_Text)_body).fontStyle = (FontStyles)(v.BodyIsBig ? 1 : 0); } SetActive(_bar, flag2 && v.Bar.HasValue); if (flag2 && v.Bar.HasValue) { _barFill.anchorMax = new Vector2(v.Bar.Value, 1f); ((Graphic)_barFillImage).color = Palette.For(v.BodyTone); } SetActive(((TMP_Text)_forecast).rectTransform, flag3); if (flag3) { UiUtil.SetText((TMP_Text)(object)_forecast, v.Forecast); ((Graphic)_forecast).color = Palette.For(v.ForecastTone); } SetActive(_root, on: true); Place(_builtFor); } public static void Hide() { if ((Object)(object)_root != (Object)null) { SetActive(_root, on: false); } } private static void SetActive(RectTransform rt, bool on) { if (((Component)rt).gameObject.activeSelf != on) { ((Component)rt).gameObject.SetActive(on); } } private static bool Ensure() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_staminaBar2Root == (Object)null || (Object)(object)((Transform)instance.m_staminaBar2Root).parent == (Object)null) { return false; } if ((Object)(object)_root != (Object)null && (Object)(object)_builtFor == (Object)(object)instance) { return true; } StarPool.Clear(); _root = UiUtil.Rect("AnglersEyePanel", ((Transform)instance.m_staminaBar2Root).parent); RectTransform root = _root; RectTransform root2 = _root; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); root2.anchorMax = val; root.anchorMin = val; _root.pivot = new Vector2(0.5f, 0f); Image obj = ((Component)_root).gameObject.AddComponent<Image>(); obj.sprite = UiUtil.Frame; obj.type = (Type)1; ((Graphic)obj).raycastTarget = false; LayoutElement obj2 = ((Component)_root).gameObject.AddComponent<LayoutElement>(); obj2.ignoreLayout = true; obj2.minWidth = 170f; VerticalLayoutGroup obj3 = ((Component)_root).gameObject.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj3).padding = new RectOffset(16, 16, 8, 10); ((HorizontalOrVerticalLayoutGroup)obj3).spacing = 0f; ((LayoutGroup)obj3).childAlignment = (TextAnchor)1; bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj3).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)obj3).childControlWidth = childControlWidth; childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandHeight = false); ((HorizontalOrVerticalLayoutGroup)obj3).childForceExpandWidth = childControlWidth; ContentSizeFitter obj4 = ((Component)_root).gameObject.AddComponent<ContentSizeFitter>(); obj4.horizontalFit = (FitMode)2; obj4.verticalFit = (FitMode)2; _titleRow = Row("Title", (Transform)(object)_root, 4f); _title = UiUtil.OutlinedText((Transform)(object)_titleRow, "Name", 20f, (TextAlignmentOptions)514); ((Graphic)_title).color = Palette.Normal; _bodyRow = Row("Body", (Transform)(object)_root, 18f); _bodyCol = UiUtil.Rect("State", (Transform)(object)_bodyRow); VerticalLayoutGroup obj5 = ((Component)_bodyCol).gameObject.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj5).spacing = 1f; ((LayoutGroup)obj5).childAlignment = (TextAnchor)4; childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj5).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)obj5).childControlWidth = childControlWidth; ((HorizontalOrVerticalLayoutGroup)obj5).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj5).childForceExpandHeight = false; _body = UiUtil.OutlinedText((Transform)(object)_bodyCol, "Text", 20f, (TextAlignmentOptions)514); _bar = UiUtil.Rect("StruggleBar", (Transform)(object)_bodyCol); Image obj6 = ((Component)_bar).gameObject.AddComponent<Image>(); obj6.sprite = UiUtil.White; ((Graphic)obj6).color = Palette.Track; ((Graphic)obj6).raycastTarget = false; LayoutElement obj7 = ((Component)_bar).gameObject.AddComponent<LayoutElement>(); float minHeight = (obj7.preferredHeight = 5f); obj7.minHeight = minHeight; _barFill = UiUtil.Rect("Fill", (Transform)(object)_bar); _barFill.anchorMin = Vector2.zero; _barFill.anchorMax = Vector2.one; val = (_barFill.offsetMin = (_barFill.offsetMax = Vector2.zero)); _barFillImage = ((Component)_barFill).gameObject.AddComponent<Image>(); _barFillImage.sprite = UiUtil.White; ((Graphic)_barFillImage).raycastTarget = false; _forecast = UiUtil.OutlinedText((Transform)(object)_bodyRow, "Forecast", 20f, (TextAlignmentOptions)514); _builtFor = instance; return true; } private static RectTransform Row(string name, Transform parent, float spacing) { RectTransform obj = UiUtil.Rect(name, parent); HorizontalLayoutGroup obj2 = ((Component)obj).gameObject.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj2).spacing = spacing; ((LayoutGroup)obj2).childAlignment = (TextAnchor)4; bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = childControlWidth; childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false); ((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = childControlWidth; return obj; } private static void Place(Hud hud) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)((Transform)_root).parent; hud.m_staminaBar2Root.GetWorldCorners(Corners); float num = (Corners[0].x + Corners[2].x) * 0.5f; float z = Corners[0].z; float num2 = Top(top: Top(top: Corners[1].y, bar: hud.m_eitrBarRoot, anim: hud.m_eitrAnimator), bar: hud.m_adrenalineBarRoot, anim: hud.m_adrenalineAnimator); Vector3 val2 = ((Transform)val).InverseTransformPoint(new Vector3(num, num2, z)); Rect rect = val.rect; Vector2 center = ((Rect)(ref rect)).center; _root.anchoredPosition = new Vector2(val2.x - center.x + PluginConfig.OffsetX.Value, val2.y - center.y + 6f + PluginConfig.OffsetY.Value); ((Transform)_root).localScale = Vector3.one * PluginConfig.Scale.Value; } private static float Top(RectTransform bar, Animator anim, float top) { if ((Object)(object)bar == (Object)null || !((Component)bar).gameObject.activeInHierarchy || (Object)(object)anim == (Object)null || !((Behaviour)anim).isActiveAndEnabled || (Object)(object)anim.runtimeAnimatorController == (Object)null || !anim.GetBool(VisibleHash)) { return top; } bar.GetWorldCorners(Corners); return Mathf.Max(top, Corners[1].y); } } internal static class FloatLabel { private const float Above = 0.8f; private const float TextSize = 20f; private const float StarSize = 18f; private static readonly List<Image> StarPool = new List<Image>(); private static RectTransform _root; private static TextMeshProUGUI _name; private static TextMeshProUGUI _bait; private static Hud _builtFor; public static void Update(FishingSnapshot s) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) if (!Features.On(Feature.FloatLabel) || (Object)(object)s.Float == (Object)null || (Object)(object)s.Catch != (Object)null || (Object)(object)s.Subject == (Object)null) { Hide(); return; } Camera mainCamera = Utils.GetMainCamera(); if ((Object)(object)mainCamera == (Object)null) { Hide(); return; } Vector3 val = Utils.WorldToScreenPointScaled(mainCamera, ((Component)s.Float).transform.position + Vector3.up * 0.8f); if (val.z < 0f || val.x < 0f || val.x > (float)Screen.width || val.y < 0f || val.y > (float)Screen.height) { Hide(); } else { if (!Ensure()) { return; } FishInfo fishInfo = FishCatalog.For(s.Subject); int quality = FishCatalog.Quality(s.Subject); string bait = s.Float.GetBait(); bool flag = false; foreach (BaitOption bait2 in fishInfo.Baits) { if (bait2.BaitId == bait) { flag = true; break; } } BaitAdvice baitAdvice = Tackle.Advise(s.Player, fishInfo); string neededBait = ((baitAdvice != null) ? baitAdvice.Best.BaitName : "?"); bool starsDrawn = UiUtil.Stars(_root, StarPool, quality, 1, 18f); UiUtil.SetText((TMP_Text)(object)_name, UiUtil.NameWithLevel(fishInfo.Name, quality, starsDrawn)); UiUtil.SiblingIndex(((TMP_Text)_bait).transform, ((Transform)_root).childCount - 1); UiUtil.SetText((TMP_Text)(object)_bait, Labels.OnFloat(flag, neededBait, UiUtil.Glyphs)); ((Graphic)_bait).color = (flag ? Palette.Normal : Palette.Bad); if (!((Component)_root).gameObject.activeSelf) { ((Component)_root).gameObject.SetActive(true); } ((Transform)_root).position = val; ((Transform)_root).localScale = Vector3.one * PluginConfig.Scale.Value; } } public static void Hide() { if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeSelf) { ((Component)_root).gameObject.SetActive(false); } } private static bool Ensure() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) Hud instance = Hud.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_crosshair == (Object)null) { return false; } if ((Object)(object)_root != (Object)null && (Object)(object)_builtFor == (Object)(object)instance) { return true; } StarPool.Clear(); _root = UiUtil.Rect("AnglersEyeFloatLabel", ((Component)instance.m_crosshair).transform.parent); _root.pivot = new Vector2(0.5f, 0f); HorizontalLayoutGroup obj = ((Component)_root).gameObject.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 3f; ((LayoutGroup)obj).childAlignment = (TextAnchor)4; bool childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true); ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = childControlWidth; childControlWidth = (((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false); ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = childControlWidth; ContentSizeFitter obj2 = ((Component)_root).gameObject.AddComponent<ContentSizeFitter>(); obj2.horizontalFit = (FitMode)2; obj2.verticalFit = (FitMode)2; _name = UiUtil.OutlinedText((Transform)(object)_root, "Name", 20f, (TextAlignmentOptions)514); ((Graphic)_name).color = Palette.Normal; _bait = UiUtil.OutlinedText((Transform)(object)_root, "Bait", 20f, (TextAlignmentOptions)514); _builtFor = instance; return true; } } internal static class Palette { public static readonly Color Normal = new Color(0.93f, 0.91f, 0.86f, 1f); public static readonly Color Good = new Color(0.56f, 0.87f, 0.42f, 1f); public static readonly Color Warn = new Color(1f, 0.71f, 0.24f, 1f); public static readonly Color Bad = new Color(0.97f, 0.43f, 0.36f, 1f); public static readonly Color Bite = new Color(1f, 0.9f, 0.3f, 1f); public static readonly Color Frame = new Color(0.08f, 0.07f, 0.06f, 0.78f); public static readonly Color FrameLine = new Color(0.78f, 0.63f, 0.31f, 0.9f); public static readonly Color Track = new Color(0f, 0f, 0f, 0.55f); public static Color For(Tone t) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) return (Color)(t switch { Tone.Good => Good, Tone.Warn => Warn, Tone.Bad => Bad, Tone.Bite => Bite, _ => Normal, }); } } internal static class UiUtil { private static Sprite _white; private static Sprite _frame; private static Glyphs _glyphs; private static TMP_FontAsset _glyphFont; private static EnemyHud _starFor; private static Sprite _star; private static Color _starColor = Color.white; private const int FrameSize = 32; private const float FrameRadius = 6f; private const float FrameLine = 1.25f; private const int FrameBorder = 8; public static Sprite White { get { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_white != (Object)null) { return _white; } Texture2D val = new Texture2D(4, 4, (TextureFormat)4, false); Color32[] array = (Color32[])(object)new Color32[16]; for (int i = 0; i < array.Length; i++) { array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); } val.SetPixels32(array); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; _white = Sprite.Create(val, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f), 4f); ((Object)_white).hideFlags = (HideFlags)61; return _white; } } public static TMP_FontAsset Font { get { Hud instance = Hud.instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.m_hoverName != (Object)null)) { return null; } return ((TMP_Text)instance.m_hoverName).font; } } public static Glyphs Glyphs { get { TMP_FontAsset f = Font; if (_glyphs == null || (Object)(object)f != (Object)(object)_glyphFont) { _glyphFont = f; _glyphs = (((Object)(object)f == (Object)null) ? Glyphs.Ascii() : Glyphs.Resolve((char c) => f.HasCharacter(c, false, false))); } return _glyphs; } } public static Sprite Frame { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_frame != (Object)null) { return _frame; } Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; Color32[] array = (Color32[])(object)new Color32[1024]; float num = 16f; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { float num2 = Mathf.Abs((float)j + 0.5f - num) - (num - 6f); float num3 = Mathf.Abs((float)i + 0.5f - num) - (num - 6f); Vector2 val2 = new Vector2(Mathf.Max(num2, 0f), Mathf.Max(num3, 0f)); float num4 = ((Vector2)(ref val2)).magnitude + Mathf.Min(Mathf.Max(num2, num3), 0f) - 6f; float num5 = Mathf.Clamp01(0.5f - num4); float num6 = Mathf.Clamp01(num4 + 1.25f + 0.5f); Color val3 = Color.Lerp(Palette.Frame, Palette.FrameLine, num6); val3.a *= num5; array[i * 32 + j] = Color32.op_Implicit(val3); } } val.SetPixels32(array); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; _frame = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(8f, 8f, 8f, 8f)); ((Object)_frame).hideFlags = (HideFlags)61; return _frame; } } public static Sprite Star { get { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) EnemyHud instance = EnemyHud.instance; if ((Object)(object)instance == (Object)null) { return null; } if ((Object)(object)_starFor != (Object)(object)instance) { _starFor = instance; _star = null; Transform val = (((Object)(object)instance.m_baseHud != (Object)null) ? instance.m_baseHud.transform.Find("level_2") : null); if ((Object)(object)val != (Object)null) { Image[] componentsInChildren = ((Component)val).GetComponentsInChildren<Image>(true); foreach (Image val2 in componentsInChildren) { if (!((Object)(object)val2.sprite == (Object)null)) { _star = val2.sprite; _starColor = ((Graphic)val2).color; break; } } } } return _star; } } public static RectTransform Rect(string name, Transform parent) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)parent).gameObject.layer }.transform; ((Transform)val).SetParent(parent, false); return val; } public static TextMeshProUGUI Text(Transform parent, string name, float size, TextAlignmentOptions align) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = Rect(name, parent); ((Component)obj).gameObject.SetActive(false); TextMeshProUGUI val = ((Component)obj).gameObject.AddComponent<TextMeshProUGUI>(); TMP_FontAsset font = Font; if ((Object)(object)font != (Object)null) { ((TMP_Text)val).font = font; } ((Component)obj).gameObject.SetActive(true); ((TMP_Text)val).fontSize = size; ((TMP_Text)val).alignment = align; ((TMP_Text)val).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)val).richText = true; ((Graphic)val).raycastTarget = false; ((Graphic)val).color = new Color(0.9f, 0.9f, 0.9f, 1f); return val; } public static TextMeshProUGUI OutlinedText(Transform parent, string name, float size, TextAlignmentOptions align) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = Text(parent, name, size, align); Hud instance = Hud.instance; TMP_Text val2 = (TMP_Text)(object)(((Object)(object)instance != (Object)null) ? instance.m_hoverName : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font == (Object)(object)((TMP_Text)val).font && (Object)(object)val2.fontSharedMaterial != (Object)null) { ((TMP_Text)val).fontSharedMaterial = val2.fontSharedMaterial; } else { ((TMP_Text)val).outlineWidth = 0.2f; ((TMP_Text)val).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); } return val; } public static bool Stars(RectTransform row, List<Image> pool, int quality, int first, float size) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) int num = quality - 1; Sprite val = ((num > 0) ? Star : null); if ((Object)(object)val == (Object)null) { num = 0; } while (pool.Count < num) { RectTransform obj = Rect("Star" + pool.Count, (Transform)(object)row); Image val2 = ((Component)obj).gameObject.AddComponent<Image>(); ((Graphic)val2).raycastTarget = false; val2.preserveAspect = true; LayoutElement obj2 = ((Component)obj).gameObject.AddComponent<LayoutElement>(); float minWidth = (obj2.preferredWidth = size); obj2.minWidth = minWidth; minWidth = (obj2.preferredHeight = size); obj2.minHeight = minWidth; pool.Add(val2); } for (int i = 0; i < pool.Count; i++) { bool flag = i < num; if (((Component)pool[i]).gameObject.activeSelf != flag) { ((Component)pool[i]).gameObject.SetActive(flag); } if (flag) { pool[i].sprite = val; ((Graphic)pool[i]).color = _starColor; SiblingIndex(((Component)pool[i]).transform, first + i); } } if (quality > 1) { return (Object)(object)val != (Object)null; } return true; } public static void SiblingIndex(Transform t, int index) { if (t.GetSiblingIndex() != index) { t.SetSiblingIndex(index); } } public static string NameWithLevel(string name, int quality, bool starsDrawn) { string text = (starsDrawn ? "" : Labels.Level(quality)); if (text.Length != 0) { return name + " " + text; } return name; } public static string Printable(string s, TMP_Text t) { if (s != null && s.IndexOf('…') >= 0 && (Object)(object)t.font != (Object)null && !t.font.HasCharacter('…', false, false)) { return s.Replace("…", "..."); } return s; } public static void SetText(TMP_Text t, string s) { s = Printable(s, t); if (t.text != s) { t.text = s; } } } } namespace AnglersEye.Patches { [HarmonyPatch] internal static class CastPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Attack), "StartDraw")] private static void StartDraw(Humanoid character, ItemData weapon) { Run(character, weapon); } [HarmonyPrefix] [HarmonyPatch(typeof(Attack), "Start")] private static void Start(Humanoid character, ItemData weapon) { Run(character, weapon); } private static void Run(Humanoid character, ItemData weapon) { try { SmartBait.BeforeCast(character, weapon); } catch (Exception e) { AnglersEyePlugin.WarnOnce("SmartBait", e); } } } [HarmonyPatch(typeof(Fish), "GetHoverText")] internal static class HoverPatch { private static void Postfix(Fish __instance, ref string __result) { try { if (Features.On(Feature.HoverInfo) && !__instance.IsOutOfWater() && !((Object)(object)Player.m_localPlayer == (Object)null)) { FishInfo info = FishCatalog.For(__instance); BaitAdvice a = Tackle.Advise(Player.m_localPlayer, info); __result += Labels.HoverSuffix(FishCatalog.Quality(__instance), a, UiUtil.Glyphs, PluginConfig.ShowOdds.Value); } } catch (Exception e) { AnglersEyePlugin.WarnOnce("HoverPatch", e); } } } [HarmonyPatch(typeof(FishingFloat), "RPC_Nibble")] internal static class NibblePatch { private static void Prefix(FishingFloat __instance, out float __state) { __state = __instance.m_nibbleTime; } private static void Postfix(FishingFloat __instance, bool correctBait, float __state) { try { if (correctBait && __instance.m_nibbleTime != __state && FishingState.IsLocal(__instance)) { BiteCue.Fire(); } } catch (Exception e) { AnglersEyePlugin.WarnOnce("NibblePatch", e); } } } [HarmonyPatch] internal static class ReelPatches { [ThreadStatic] private static bool _suppressBlock; [HarmonyPrefix] [HarmonyPatch(typeof(FishingFloat), "FixedUpdate")] private static void FloatUpdatePrefix(FishingFloat __instance) { _suppressBlock = false; try { if (Features.On(Feature.SmartReel) && FishingState.IsLocal(__instance)) { Fish val = __instance.GetCatch(); _suppressBlock = ReelPolicy.SuppressReel(smartReel: true, (Object)(object)val != (Object)null, (Object)(object)val != (Object)null && val.IsEscaping()); } } catch (Exception e) { AnglersEyePlugin.WarnOnce("SmartReel", e); } } [HarmonyFinalizer] [HarmonyPatch(typeof(FishingFloat), "FixedUpdate")] private static void FloatUpdateFinalizer() { _suppressBlock = false; } [HarmonyPrefix] [HarmonyPatch(typeof(Humanoid), "IsBlocking")] private static bool IsBlockingPrefix(ref bool __result) { if (!_suppressBlock) { return true; } __result = false; return false; } [HarmonyPrefix] [HarmonyPatch(typeof(FishingFloat), "TryToHook")] private static bool TryToHookPrefix(FishingFloat __instance) { try { if (!Features.On(Feature.HookWindow) || !FishingState.IsLocal(__instance)) { return true; } float window = ReelPolicy.HookWindow(extended: true, PluginConfig.HookWindowSeconds.Value); if ((Object)(object)__instance.m_nibbler != (Object)null && ReelPolicy.InHookWindow(Time.time, __instance.m_nibbleTime, window) && (Object)(object)__instance.GetCatch() == (Object)null) { __instance.Message("$msg_fishing_hooked", true); __instance.SetCatch(__instance.m_nibbler); __instance.m_nibbler = null; Game.instance.IncrementPlayerStat((PlayerStatType)158, 1f, false); } return false; } catch (Exception e) { AnglersEyePlugin.WarnOnce("HookWindow", e); return true; } } } } namespace AnglersEye.Core { internal static class Compat { public static CompatVerdict Verdict { get; private set; } public static bool IsOff(Feature f) { if (Verdict != null) { return Verdict.IsOff(f); } return false; } public static void EnsureEvaluated() { if (Verdict != null || (Object)(object)ZNetScene.instance == (Object)null) { return; } HashSet<string> pluginGuids = new HashSet<string>(Chainloader.PluginInfos.Keys); Dictionary<string, IList<string>> foreignPatchOwners = new Dictionary<string, IList<string>> { { "FishingFloat.FixedUpdate", Owners(typeof(FishingFloat), "FixedUpdate") }, { "FishingFloat.TryToHook", Owners(typeof(FishingFloat), "TryToHook") }, { "Fish.GetStaminaUse", Owners(typeof(Fish), "GetStaminaUse") } }; Verdict = CompatRules.Evaluate(pluginGuids, foreignPatchOwners); foreach (string reason in Verdict.Reasons) { AnglersEyePlugin.Log.LogInfo((object)("Angler's Eye compat: " + reason)); } } private static IList<string> Owners(Type type, string method) { List<string> list = new List<string>(); MethodBase methodBase = AccessTools.Method(type, method, (Type[])null, (Type[])null); Patches val = ((methodBase != null) ? Harmony.GetPatchInfo(methodBase) : null); if (val == null) { return list; } foreach (string owner in val.Owners) { if (owner != "com.jumpingmushroom.anglerseye" && !list.Contains(owner)) { list.Add(owner); } } return list; } } internal static class ConsoleCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static Func<FishInfo, string> <>9__3_0; public static Func<BaitOption, string> <>9__3_1; internal void <Register>b__0_0(ConsoleEventArgs args) { if (((args.Length > 1) ? args[1].ToLowerInvariant() : "") == "fish") { Fish(args.Context); } else { Status(args.Context); } } internal string <Fish>b__3_0(FishInfo f) { return f.Prefab; } internal string <Fish>b__3_1(BaitOption b) { return b.BaitName + " " + Labels.Odds(b.Chance); } } public static void Register() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { if (((args.Length > 1) ? args[1].ToLowerInvariant() : "") == "fish") { Fish(args.Context); } else { Status(args.Context); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("anglerseye", "Angler's Eye: feature and compat status (fish = bait table for every fish)", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } internal static void Say(Terminal ctx, string line) { if ((Object)(object)ctx != (Object)null) { ctx.AddString(line); } AnglersEyePlugin.Log.LogInfo((object)line); } private static void Status(Terminal ctx) { try { Say(ctx, "Angler's Eye 0.1.0" + (PluginConfig.Enabled.Value ? "" : " (disabled)") + ": " + FishCatalog.All.Count + " fish known."); List<string> list = new List<string>(); List<string> list2 = new List<string>(); foreach (Feature value in Enum.GetValues(typeof(Feature))) { if (value != Feature.None) { (Features.On(value) ? list : list2).Add(CompatRules.Describe(value)); } } Say(ctx, "Angler's Eye: on: " + ((list.Count > 0) ? string.Join(", ", list) : "nothing")); Say(ctx, "Angler's Eye: off: " + ((list2.Count > 0) ? string.Join(", ", list2) : "nothing")); if (Compat.Verdict == null) { Say(ctx, "Angler's Eye: compat: not checked yet (load a world)"); return; } if (Compat.Verdict.Reasons.Count == 0) { Say(ctx, "Angler's Eye: compat: no other fishing mods found"); return; } foreach (string reason in Compat.Verdict.Reasons) { Say(ctx, "Angler's Eye: compat: " + reason); } } catch (Exception ex) { Say(ctx, "Angler's Eye: couldn't show status: " + ex.Message); AnglersEyePlugin.WarnOnce("anglerseye status", ex); } } private static void Fish(Terminal ctx) { try { RodParams rod = FishCatalog.Rod; Say(ctx, "Angler's Eye: float: reel " + rod.PullStaminaUse + "/s x" + rod.PullStaminaUseMaxSkillMultiplier + " at max skill, speed " + rod.PullLineSpeed + "-" + rod.PullLineSpeedMaxSkill + " m/s, hooked drain " + rod.HookedStaminaPerSec + "-" + rod.HookedStaminaPerSecMaxSkill + "/s, range " + FishCatalog.FloatRange + "m"); foreach (FishInfo item in FishCatalog.All.Values.OrderBy<FishInfo, string>((FishInfo f) => f.Prefab, StringComparer.Ordinal)) { FishParams fishParams = item.Params; string text = ((item.Baits.Count == 0) ? "no bait" : string.Join(", ", item.Baits.Select((BaitOption b) => b.BaitName + " " + Labels.Odds(b.Chance)))); Say(ctx, "Angler's Eye: " + item.Prefab + " " + item.Name + ": " + text + " | stamina " + fishParams.StaminaUse + "/" + fishParams.EscapeStaminaUse + ", struggle " + fishParams.EscapeMin + "-" + fishParams.EscapeMax + " +" + fishParams.EscapeMaxPerLevel + "/level, pause " + fishParams.EscapeWaitMin + "-" + fishParams.EscapeWaitMax + "s"); } } catch (Exception ex) { Say(ctx, "Angler's Eye: couldn't list fish: " + ex.Message); AnglersEyePlugin.WarnOnce("anglerseye fish", ex); } } } internal static class Features { public static bool On(Feature f) { if (PluginConfig.Enabled.Value && PluginConfig.Toggle(f)) { return !Compat.IsOff(f); } return false; } } internal sealed class FishInfo { public string Prefab; public string NameToken; public List<BaitOption> Baits; public FishParams Params; public string Name { get { if (Localization.instance == null) { return NameToken; } return Localization.instance.Localize(NameToken); } } } internal static class FishCatalog { private static readonly Dictionary<string, FishInfo> ByPrefab = new Dictionary<string, FishInfo>(); private static readonly HashSet<string> BaitAmmoTypes = new HashSet<string>(); private static ZNetScene _builtFor; public static RodParams Rod = new RodParams(); public static float FloatRange = 10f; public static float MaxDistance = 30f; public static IReadOnlyDictionary<string, FishInfo> All { get { EnsureBuilt(); return ByPrefab; } } public static void EnsureBuilt() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance == (Object)(object)_builtFor) { return; } ByPrefab.Clear(); BaitAmmoTypes.Clear(); foreach (GameObject prefab in instance.m_prefabs) { if (!((Object)(object)prefab == (Object)null)) { Fish component = prefab.GetComponent<Fish>(); if ((Object)(object)component != (Object)null) { ByPrefab[((Object)prefab).name] = Build(((Object)prefab).name, component); } FishingFloat component2 = prefab.GetComponent<FishingFloat>(); if ((Object)(object)component2 != (Object)null) { ReadFloat(component2); } } } _builtFor = instance; } public static FishInfo For(Fish f) { EnsureBuilt(); string prefabName = Utils.GetPrefabName(((Component)f).gameObject); if (!ByPrefab.TryGetValue(prefabName, out var value)) { value = Build(prefabName, f); ByPrefab[prefabName] = value; } return value; } public static bool IsBaitAmmoType(string ammoType) { EnsureBuilt(); if (!string.IsNullOrEmpty(ammoType)) { return BaitAmmoTypes.Contains(ammoType); } return false; } public static int Quality(Fish f) { ItemDrop val = (((Object)(object)f.m_itemDrop != (Object)null) ? f.m_itemDrop : ((Component)f).GetComponent<ItemDrop>()); if (!((Object)(object)val != (Object)null)) { return 1; } return Math.Max(1, val.m_itemData.m_quality); } private static FishInfo Build(string prefab, Fish f) { List<BaitOption> list = new List<BaitOption>(); foreach (BaitSetting bait in f.m_baits) { if (bait != null && !((Object)(object)bait.m_bait == (Object)null)) { string name = bait.m_bait.m_itemData.m_shared.m_name; string baitName = ((Localization.instance != null) ? Localization.instance.Localize(name) : name); list.Add(new BaitOption(((Object)bait.m_bait).name, baitName, bait.m_chance)); string ammoType = bait.m_bait.m_itemData.m_shared.m_ammoType; if (!string.IsNullOrEmpty(ammoType)) { BaitAmmoTypes.Add(ammoType); } } } return new FishInfo { Prefab = prefab, NameToken = f.m_name, Baits = list, Params = new FishParams { StaminaUse = f.m_staminaUse, EscapeStaminaUse = f.m_escapeStaminaUse, EscapeMin = f.m_escapeMin, EscapeMax = f.m_escapeMax, EscapeMaxPerLevel = f.m_escapeMaxPerLevel, EscapeWaitMin = f.m_escapeWaitMin, EscapeWaitMax = f.m_escapeWaitMax } }; } private static void ReadFloat(FishingFloat ff) { Rod = new RodParams { PullStaminaUse = ff.m_pullStaminaUse, PullStaminaUseMaxSkillMultiplier = ff.m_pullStaminaUseMaxSkillMultiplier, PullLineSpeed = ff.m_pullLineSpeed, PullLineSpeedMaxSkill = ff.m_pullLineSpeedMaxSkill, HookedStaminaPerSec = ff.m_hookedStaminaPerSec, HookedStaminaPerSecMaxSkill = ff.m_hookedStaminaPerSecMaxSkill }; FloatRange = ff.m_range; MaxDistance = ff.m_maxDistance; } } internal sealed class FishingSnapshot { public Player Player; public ItemData Rod; public bool RodEquipped; public FishingFloat Float; public Fish Catch; public bool Escaping; public float RemainingEscape; public float EscapeTotal; public float LineLength; public Fish Subject; } internal static class FishingState { public static readonly FishingSnapshot Current = new FishingSnapshot(); private const float NearbyRadius = 10f; private static readonly StruggleTracker Struggle = new StruggleTracker(); public static bool IsLocal(FishingFloat ff) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ff == (Object)null || (Object)(object)ff.m_nview == (Object)null || !ff.m_nview.IsValid()) { return false; } long num = ff.m_nview.GetZDO().GetLong(ZDOVars.s_rodOwner, 0L); ZDOID zDOID = ((Character)localPlayer).GetZDOID(); return num == ((ZDOID)(ref zDOID)).UserID; } public static FishingFloat LocalFloat() { foreach (FishingFloat allInstance in FishingFloat.GetAllInstances()) { if (IsLocal(allInstance)) { return allInstance; } } return null; } public static void Refresh() { FishingSnapshot current = Current; Player val = (current.Player = Player.m_localPlayer); current.Rod = (((Object)(object)val != (Object)null) ? ((Humanoid)val).GetCurrentWeapon() : null); current.RodEquipped = Tackle.IsRod(current.Rod); current.Float = (((Object)(object)val != (Object)null) ? LocalFloat() : null); current.Catch = (((Object)(object)current.Float != (Object)null) ? current.Float.GetCatch() : null); current.Escaping = (Object)(object)current.Catch != (Object)null && current.Catch.IsEscaping(); current.RemainingEscape = (((Object)(object)current.Catch != (Object)null) ? Mathf.Max(0f, current.Catch.m_escapeTime) : 0f); current.EscapeTotal = Struggle.Update((Object)(object)current.Catch != (Object)null, current.Escaping, current.RemainingEscape); current.LineLength = (((Object)(object)current.Float != (Object)null) ? current.Float.m_lineLength : 0f); current.Subject = (((Object)(object)current.Float != (Object)null && (Object)(object)current.Catch == (Object)null) ? SubjectFor(current.Float) : null); } private static Fish SubjectFor(FishingFloat ff) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ff.m_nibbler != (Object)null && Time.time - ff.m_nibbleTime < 2f) { return ff.m_nibbler; } Fish result = null; float num = Mathf.Min(ff.m_range, 10f); Vector3 position = ((Component)ff).transform.position; foreach (IMonoUpdater instance in Fish.Instances) { Fish val = (Fish)(object)((instance is Fish) ? instance : null); if (!((Object)(object)val == (Object)null) && !val.IsOutOfWater()) { if ((Object)(object)val.m_waypointFF == (Object)(object)ff) { return val; } float num2 = Vector3.Distance(((Component)val).transform.position, position); if (num2 <= num) { num = num2; result = val; } } } return result; } } internal static class Runtime { private const float AimInterval = 0.25f; private static float _nextAim; private static FishSighting _aim; private static BaitAdvice _aimAdvice; public static void Tick() { try { FishCatalog.EnsureBuilt(); Compat.EnsureEvaluated(); FishingState.Refresh(); UpdatePanel(FishingState.Current); FloatLabel.Update(FishingState.Current); } catch (Exception e) { AnglersEyePlugin.WarnOnce("Runtime.Tick", e); FishingPanel.Hide(); FloatLabel.Hide(); } } private static void UpdatePanel(FishingSnapshot s) { if (!PluginConfig.Enabled.Value || (Object)(object)s.Player == (Object)null || !s.RodEquipped) { FishingPanel.Hide(); return; } if ((Object)(object)s.Catch != (Object)null) { Verdict? v = (Features.On(Feature.Forecast) ? new Verdict?(Forecast(s, s.Catch, hooked: true)) : ((Verdict?)null)); FishingPanel.Show(PanelView.Hooked(FishCatalog.For(s.Catch).Name, FishCatalog.Quality(s.Catch), s.Escaping, Features.On(Feature.StruggleIndicator), s.RemainingEscape, s.EscapeTotal, v)); return; } if ((Object)(object)s.Float != (Object)null && BiteCue.Active) { FishingPanel.Show(((Object)(object)s.Subject != (Object)null) ? PanelView.Bite(FishCatalog.For(s.Subject).Name, FishCatalog.Quality(s.Subject)) : PanelView.Bite(null, 0)); return; } if ((Object)(object)s.Float != (Object)null) { if (!Features.On(Feature.Forecast) && !Features.On(Feature.FloatLabel)) { FishingPanel.Hide(); return; } if ((Object)(object)s.Subject == (Object)null) { FishingPanel.Show(PanelView.Waiting(null, 0, null)); return; } Verdict? v2 = (Features.On(Feature.Forecast) ? new Verdict?(Forecast(s, s.Subject, hooked: false)) : ((Verdict?)null)); FishingPanel.Show(PanelView.Waiting(FishCatalog.For(s.Subject).Name, FishCatalog.Quality(s.Subject), v2)); return; } if (!Features.On(Feature.SmartBait)) { FishingPanel.Hide(); return; } if (Time.time >= _nextAim) { _nextAim = Time.time + 0.25f; _aim = Targeting.Aim(s.Player); _aimAdvice = ((_aim != null && FishCatalog.All.TryGetValue(_aim.Species, out var value)) ? Tackle.Advise(s.Player, value) : null); } if (_aim == null || !FishCatalog.All.TryGetValue(_aim.Species, out var value2)) { FishingPanel.Hide(); } else { FishingPanel.Show(PanelView.Target(value2.Name, _aim.Quality, _aimAdvice, PluginConfig.ShowOdds.Value)); } } private static Verdict Forecast(FishingSnapshot s, Fish f, bool hooked) { FishInfo fishInfo = FishCatalog.For(f); return CatchForecast.Estimate(FishCatalog.Rod, fishInfo.Params, FishCatalog.Quality(f), s.LineLength, ((Character)s.Player).GetSkillFactor((SkillType)104), s.Player.GetStamina(), hooked, hooked ? s.RemainingEscape : 0f).Verdict; } } internal static class SmartBait { private const float RepeatMessageAfter = 15f; private static string _lastMessageText; private static float _lastMessageTime = -15f; public static void BeforeCast(Humanoid character, ItemData weapon) { Player val = (Player)(object)((character is Player) ? character : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer || !Features.On(Feature.SmartBait) || !Tackle.IsRod(weapon)) { return; } FishSighting fishSighting = Targeting.Aim(val); if (fishSighting == null || !FishCatalog.All.TryGetValue(fishSighting.Species, out var value)) { return; } BaitAdvice baitAdvice = Tackle.Advise(val, value); if (baitAdvice == null) { return; } if (!baitAdvice.Carried) { string text = Labels.NeedsBait(value.Name, baitAdvice.Best.BaitName); if (text != _lastMessageText || Time.time - _lastMessageTime > 15f) { _lastMessageText = text; _lastMessageTime = Time.time; ((Character)val).Message((MessageType)2, text, 0, (Sprite)null, false); } return; } string ammoType = weapon.m_shared.m_ammoType; ItemData val2 = VanillaChoice(val, ammoType); if (val2 != null && (Object)(object)val2.m_dropPrefab != (Object)null && ((Object)val2.m_dropPrefab).name == baitAdvice.Best.BaitId) { return; } ItemData ammoItem = ((Humanoid)val).GetInventory().GetAmmoItem(ammoType, baitAdvice.Best.BaitId); if (ammoItem != null) { bool flag = ((Humanoid)val).EquipItem(ammoItem, false); if (PluginConfig.Verbose.Value) { AnglersEyePlugin.Log.LogInfo((object)("Angler's Eye: smart bait " + value.Prefab + " -> " + baitAdvice.Best.BaitId + (flag ? "" : " (equip refused)"))); } } } private static ItemData VanillaChoice(Player p, string type) { ItemData ammoItem = ((Humanoid)p).GetAmmoItem(); if (ammoItem != null && ((Humanoid)p).GetInventory().ContainsItem(ammoItem) && ammoItem.m_shared.m_ammoType == type) { return ammoItem; } return ((Humanoid)p).GetInventory().GetAmmoItem(type, (string)null); } } internal static class Tackle { private static readonly Dictionary<string, int> _carried = new Dictionary<string, int>(); private static int _carriedFrame = -1; private static Player _carriedFor; public static bool IsRod(ItemData w) { if (w != null) { return FishCatalog.IsBaitAmmoType(w.m_shared.m_ammoType); } return false; } public static IReadOnlyDictionary<string, int> Carried(Player p) { if (_carriedFrame == Time.frameCount && (Object)(object)_carriedFor == (Object)(object)p) { return _carried; } _carried.Clear(); foreach (ItemData allItem in ((Humanoid)p).GetInventory().GetAllItems()) { if (!((Object)(object)allItem.m_dropPrefab == (Object)null)) { _carried.TryGetValue(((Object)allItem.m_dropPrefab).name, out var value); _carried[((Object)allItem.m_dropPrefab).name] = value + allItem.m_stack; } } _carriedFrame = Time.frameCount; _carriedFor = p; return _carried; } public static BaitAdvice Advise(Player p, FishInfo info) { if (info != null) { return BaitAdvisor.Advise(info.Baits, Carried(p)); } return null; } } internal static class Targeting { private static readonly List<FishSighting> Buffer = new List<FishSighting>(); public static FishSighting Aim(Player p) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) GameCamera instance = GameCamera.instance; if ((Object)(object)p == (Object)null || (Object)(object)instance == (Object)null) { return null; } FishSighting crosshair = null; GameObject hoverObject = ((Humanoid)p).GetHoverObject(); Fish val = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent<Fish>() : null); if ((Object)(object)val != (Object)null && !val.IsOutOfWater()) { crosshair = Sight(val); } Buffer.Clear(); foreach (IMonoUpdater instance2 in Fish.Instances) { Fish val2 = (Fish)(object)((instance2 is Fish) ? instance2 : null); if (!((Object)(object)val2 == (Object)null) && !val2.IsOutOfWater() && !val2.IsHooked()) { Buffer.Add(Sight(val2)); } } Transform transform = ((Component)instance).transform; return TargetPicker.Pick(crosshair, V(transform.position), V(transform.forward), PluginConfig.AimConeDegrees.Value, FishCatalog.MaxDistance, Buffer); } public static FishSighting Sight(Fish f) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) return new FishSighting(Utils.GetPrefabName(((Component)f).gameObject), FishCatalog.Quality(f), V(((Component)f).transform.position)); } private static Vec3 V(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vec3(v.x, v.y, v.z); } } } namespace AnglersEye.Core.Model { public sealed class BaitOption { public readonly string BaitId; public readonly string BaitName; public readonly float Chance; public BaitOption(string baitId, string baitName, float chance) { BaitId = baitId; BaitName = baitName; Chance = chance; } } public sealed class BaitAdvice { public BaitOption Best; public bool Carried; public int CarriedCount; } public static class BaitAdvisor { public static BaitAdvice Advise(IReadOnlyList<BaitOption> table, IReadOnlyDictionary<string, int> carried) { List<BaitOption> list = Merge(table); if (list.Count == 0) { return null; } list.Sort(delegate(BaitOption a, BaitOption b) { if (a.Chance == b.Chance) { return string.CompareOrdinal(a.BaitId, b.BaitId); } float chance = b.Chance; return chance.CompareTo(a.Chance); }); BaitOption baitOption = null; int num = 0; foreach (BaitOption item in list) { if (carried != null && carried.TryGetValue(item.BaitId, out var value) && value > 0 && (baitOption == null || item.Chance > baitOption.Chance || (item.Chance == baitOption.Chance && value > num))) { baitOption = item; num = value; } } if (baitOption != null) { return new BaitAdvice { Best = baitOption, Carried = true, CarriedCount = num }; } return new BaitAdvice { Best = list[0], Carried = false, CarriedCount = 0 }; } private static List<BaitOption> Merge(IReadOnlyList<BaitOption> table) { List<BaitOption> list = new List<BaitOption>(); if (table == null) { return list; } Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (BaitOption item in table) { if (item != null && !string.IsNullOrEmpty(item.BaitId)) { if (dictionary.TryGetValue(item.BaitId, out var value)) { BaitOption baitOption = list[value]; list[value] = new BaitOption(baitOption.BaitId, baitOption.BaitName, 1f - (1f - baitOption.Chance) * (1f - item.Chance)); } else { dictionary[item.BaitId] = list.Count; list.Add(item); } } } return list; } } public sealed class RodParams { public float PullStaminaUse = 10f; public float PullStaminaUseMaxSkillMultiplier = 0.2f; public float PullLineSpeed = 1f; public float PullLineSpeedMaxSkill = 2f; public float HookedStaminaPerSec = 1f; public float HookedStaminaPerSecMaxSkill = 0.2f; } public sealed class FishParams { public float StaminaUse = 1f; public float EscapeStaminaUse = 2f; public float EscapeMin = 0.5f; public float EscapeMax = 3f; public float EscapeMaxPerLevel = 1.5f; public float EscapeWaitMin = 0.75f; public float EscapeWaitMax = 4f; } public enum Verdict { Likely, Tight, Unlikely } public struct Forecast { public readonly float Cost; public readonly Verdict Verdict; public Forecast(float cost, Verdict verdict) { Cost = cost; Verdict = verdict; } } public static class CatchForecast { public const float LandedAt = 0.5f; public const float Margin = 1.25f; public static Forecast Estimate(RodParams rod, FishParams fish, int quality, float lineLength, float skillFactor, float stamina, bool hooked, float remainingEscape) { float t = Math.Min(1f, Math.Max(0f, skillFactor)); int num = Math.Max(1, quality); float val = Lerp(rod.PullLineSpeed, rod.PullLineSpeedMaxSkill, t); float num2 = Math.Max(0f, lineLength - 0.5f) / Math.Max(0.0001f, val); float num3 = Math.Max(0f, (fish.EscapeMin + fish.EscapeMax + (float)num * fish.EscapeMaxPerLevel) / 2f); float num4 = (fish.EscapeWaitMin + fish.EscapeWaitMax) / 2f; float num5 = (hooked ? Math.Max(0f, remainingEscape) : num3); if (num2 > 0f) { if (num4 <= 0f) { return new Forecast(float.PositiveInfinity, Verdict.Unlikely); } float num6 = num4 / (num4 + num3); num5 += num2 / num6; } float num7 = rod.PullStaminaUse + fish.StaminaUse * (float)num; float num8 = Lerp(num7, num7 * rod.PullStaminaUseMaxSkillMultiplier, t) * num2; float num9 = Lerp(rod.HookedStaminaPerSec, rod.HookedStaminaPerSecMaxSkill, t) * num5; float cost = num8 + num9; return new Forecast(cost, Judge(cost, stamina)); } public static Verdict Judge(float cost, float stamina) { if (stamina >= cost * 1.25f) { return Verdict.Likely; } if (!(stamina >= cost)) { return Verdict.Unlikely; } return Verdict.Tight; } private static float Lerp(float a, float b, float t) { return a + (b - a) * t; } } [Flags] public enum Feature { None = 0, HoverInfo = 1, FloatLabel = 2, SmartBait = 4, BiteCue = 8, StruggleIndicator = 0x10, Forecast = 0x20, SmartReel = 0x40, HookWindow = 0x80 } public sealed class CompatVerdict { public Feature Disabled; public readonly List<string> Reasons = new List<string>(); public bool IsOff(Feature f) { return (Disabled & f) != 0; } } public static class CompatRules { public const string Hooked = "Azumatt.Hooked"; public const string TrollingFishing = "sighsorry.TrollingFishing"; public const string FixedUpdate = "FishingFloat.FixedUpdate"; public const string TryToHook = "FishingFloat.TryToHook"; public const string GetStaminaUse = "Fish.GetStaminaUse"; private static readonly KeyValuePair<Feature, string>[] Names = new KeyValuePair<Feature, string>[8] { new KeyValuePair<Feature, string>(Feature.HoverInfo, "hover info"), new KeyValuePair<Feature, string>(Feature.FloatLabel, "float label"), new KeyValuePair<Feature, string>(Feature.SmartBait, "smart bait"), new KeyValuePair<Feature, string>(Feature.BiteCue, "bite cue"), new KeyValuePair<Feature, string>(Feature.StruggleIndicator, "struggle indicator"), new KeyValuePair<Feature, string>(Feature.Forecast, "forecast"), new KeyValuePair<Feature, string>(Feature.SmartReel, "smart reel"), new KeyValuePair<Feature, string>(Feature.HookWindow, "extended hook window") }; public static CompatVerdict Evaluate(ICollection<string> pluginGuids, IDictionary<string, IList<string>> foreignPatchOwners) { CompatVerdict compatVerdict = new CompatVerdict(); if (pluginGuids.Contains("Azumatt.Hooked")) { Off(compatVerdict, Feature.SmartBait | Feature.BiteCue | Feature.StruggleIndicator | Feature.Forecast | Feature.SmartReel | Feature.HookWindow, "Hooked (Azumatt.Hooked)"); } if (pluginGuids.Contains("sighsorry.TrollingFishing")) { Off(compatVerdict, Feature.SmartBait | Feature.Forecast | Feature.SmartReel | Feature.HookWindow, "Trolling Fishing (sighsorry.TrollingFishing)"); } Patched(compatVerdict, foreignPatchOwners, "FishingFloat.FixedUpdate", Feature.SmartReel); Patched(compatVerdict, foreignPatchOwners, "FishingFloat.TryToHook", Feature.HookWindow); Patched(compatVerdict, foreignPatchOwners, "Fish.GetStaminaUse", Feature.Forecast); return compatVerdict; } public static string Describe(Feature f) { List<string> list = new List<string>(); KeyValuePair<Feature, string>[] names = Names; for (int i = 0; i < names.Length; i++) { KeyValuePair<Feature, string> keyValuePair = names[i]; if ((f & keyValuePair.Key) != Feature.None) { list.Add(keyValuePair.Value); } } return string.Join(", ", list); } private static void Patched(CompatVerdict v, IDictionary<string, IList<string>> owners, string method, Feature f) { if (owners != null && owners.TryGetValue(method, out var value) && value != null && value.Count > 0) { Off(v, f, string.Join(", ", value) + " patches " + method); } } private static void Off(CompatVerdict v, Feature f, string who) { v.Disabled |= f; v.Reasons.Add(who + ": " + Describe(f) + " off"); } } public sealed class Glyphs { public string Star; public string Yes; public string No; public string Sep; public static Glyphs Unicode() { return new Glyphs { Star = "★", Yes = "✔", No = "✖", Sep = "·" }; } public static Glyphs Ascii() { return new Glyphs { Star = "*", Yes = "+", No = "x", Sep = "-" }; } public static Glyphs Resolve(Func<char, bool> hasChar) { Glyphs glyphs = Unicode(); Glyphs glyphs2 = Ascii(); return new Glyphs { Star = Pick(glyphs.Star, glyphs2.Star, hasChar), Yes = Pick(glyphs.Yes, glyphs2.Yes, hasChar), No = Pick(glyphs.No, glyphs2.No, hasChar), Sep = Pick(glyphs.Sep, glyphs2.Sep, hasChar) }; } private static string Pick(string unicode, string ascii, Func<char, bool> hasChar) { foreach (char arg in unicode) { if (!hasChar(arg)) { return ascii; } } return unicode; } } public static class Labels { public static string Stars(int quality, Glyphs g) { if (quality <= 1) { return ""; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 1; i < quality; i++) { stringBuilder.Append(g.Star); } return stringBuilder.ToString(); } public static string Level(int quality) { if (quality <= 1) { return ""; } return "Lv " + quality; } public static string Odds(float chance) { return (int)Math.Round(chance * 100f) + "%"; } public static string Bait(BaitAdvice a, Glyphs g, bool showOdds) { string text = a.Best.BaitName + (showOdds ? (" " + Odds(a.Best.Chance)) : ""); if (!a.Carried) { return text + " " + g.No; } return text + " " + g.Yes + " (x" + a.CarriedCount + ")"; } public static string HoverSuffix(int quality, BaitAdvice a, Glyphs g, bool showOdds) { string text = ((quality > 1) ? (" " + Stars(quality, g)) : ""); if (a != null) { text = text + "\nBait: " + Bait(a, g, showOdds); } return text; } public static string OnFloat(bool baitWorks, string neededBait, Glyphs g) { return " " + g.Sep + (baitWorks ? " bait ok" : (" needs " + neededBait)); } public static string NeedsBait(string fish, string bait) { return "Angler's Eye: " + fish + " needs " + bait; } } public enum Tone { Normal, Good, Warn, Bad, Bite } public sealed class PanelView { public const string WaitingText = "Waiting…"; public readonly string Title; public readonly int Level; public readonly string Body; public readonly Tone BodyTone; public readonly bool BodyIsBig; public readonly float? Bar; public readonly string Forecast; public readonly Tone ForecastTone; private PanelView(string title, int level, string body, Tone bodyTone, bool bodyIsBig, float? bar, Verdict? v) { Title = title; Level = level; Body = body; BodyTone = bodyTone; BodyIsBig = bodyIsBig; Bar = bar; Forecast = (v.HasValue ? WordFor(v.Value) : null); ForecastTone = (v.HasValue ? ToneFor(v.Value) : Tone.Normal); } public static PanelView Target(string name, int quality, BaitAdvice a, bool showOdds) { if (a == null) { return new PanelView(name, quality, null, Tone.Normal, bodyIsBig: false, null, null); } string text = a.Best.BaitName + (showOdds ? (" " + Labels.Odds(a.Best.Chance)) : ""); if (!a.Carried) { return new PanelView(name, quality, "needs " + text, Tone.Bad, bodyIsBig: false, null, null); } return new PanelView(name, quality, text + " (" + a.CarriedCount + ")", Tone.Normal, bodyIsBig: false, null, null); } public static PanelView Waiting(string name, int quality, Verdict? v) { if (name == null) { return new PanelView("Waiting…", 0, null, Tone.Normal, bodyIsBig: false, null, null); } return new PanelView(name, quality, "Waiting…", Tone.Normal, bodyIsBig: false, null, v); } public static PanelView Bite(string name, int quality) { return new PanelView(name, (name != null) ? quality : 0, "BITE!", Tone.Bite, bodyIsBig: true, null, null); } public static PanelView Hooked(string name, int quality, bool struggling, bool showStruggle, float remainingEscape, float escapeTotal, Verdict? v) { if (!showStruggle) { if (!v.HasValue) { return null; } return new PanelView(name, quality, null, Tone.Normal, bodyIsBig: false, null, v); } if (struggling) { return new PanelView(name, quality, "WAIT", Tone.Warn, bodyIsBig: true, StruggleFraction(remainingEscape, escapeTotal), v); } return new PanelView(name, quality, "REEL", Tone.Good, bodyIsBig: true, null, v); } public static float StruggleFraction(float remaining, float total) { if (total <= 0f) { return 0f; } return Math.Min(1f, Math.Max(0f, remaining / total)); } public static string WordFor(Verdict v) { return v switch { Verdict.Likely => "can land", Verdict.Tight => "tight", _ => "unlikely", }; } public static Tone ToneFor(Verdict v) { return v switch { Verdict.Likely => Tone.Good, Verdict.Tight => Tone.Warn, _ => Tone.Bad, }; } } public static class ReelPolicy { public const float VanillaHookWindow = 0.5f; public const float MaxHookWindow = 1.5f; public static bool SuppressReel(bool smartReel, bool hooked, bool escaping) { return smartReel && hooked && escaping; } public static float HookWindow(bool extended, float seconds) { if (!extended) { return 0.5f; } return Math.Min(1.5f, Math.Max(0.5f, seconds)); } public static bool InHookWindow(float now, float nibbleTime, float window) { return now - nibbleTime < window; } } public sealed class StruggleTracker { private bool _wasEscaping; private float _lastRemaining; public float Total { get; private set; } public float Update(bool hasCatch, bool escaping, float remaining) { if (!hasCatch) { Total = 0f; _wasEscaping = false; _lastRemaining = 0f; return Total; } if (escaping && (!_wasEscaping || remaining > _lastRemaining)) { Total = remaining; } _wasEscaping = escaping; _lastRemaining = remaining; return Total; } } public sealed class FishSighting { public readonly string Species; public readonly int Quality; public readonly Vec3 Position; public FishSighting(string species, int quality, Vec3 position) { Species = species; Quality = quality; Position = position; } } public static class TargetPicker { public static FishSighting Pick(FishSighting crosshair, Vec3 origin, Vec3 aim, float coneDegrees, float range, IReadOnlyList<FishSighting> fish) { if (crosshair != null) { return crosshair; } if (fish == null || fish.Count == 0) { return null; } return InCone(origin, aim, coneDegrees, range, fish) ?? MostCommon(origin, range, fish); } private static FishSighting InCone(Vec3 origin, Vec3 aim, float coneDegrees, float range, IReadOnlyList<FishSighting> fish) { float num = (float)Math.Sqrt(aim.X * aim.X + aim.Z * aim.Z); if (num < 0.0001f) { return null; } double num2 = Math.Cos((double)coneDegrees * Math.PI / 180.0); FishSighting result = null; float num3 = float.MaxValue; foreach (FishSighting item in fish) { Vec3 vec = item.Position - origin; float length = vec.Length; if (!(length > range)) { float lengthXZ = vec.LengthXZ; if (((lengthXZ < 0.0001f) ? 1.0 : ((double)((vec.X * aim.X + vec.Z * aim.Z) / (lengthXZ * num)))) >= num2 && length < num3) { result = item; num3 = length; } } } return result; } private static FishSighting MostCommon(Vec3 origin, float range, IReadOnlyList<FishSighting> fish) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); Dictionary<string, FishSighting> dictionary2 = new Dictionary<string, FishSighting>(); Dictionary<string, float> dictionary3 = new Dictionary<string, float>(); foreach (FishSighting item in fish) { float length = (item.Position - origin).Length; if (!(length > range)) { dictionary.TryGetValue(item.Species, out var value); dictionary[item.Species] = value + 1; if (!dictionary3.TryGetValue(item.Species, out var value2) || length < value2) { dictionary3[item.Species] = length; dictionary2[item.Species] = item; } } } FishSighting fishSighting = null; int num = 0; float num2 = 0f; foreach (KeyValuePair<string, int> item2 in dictionary) { float num3 = dictionary3[item2.Key]; if (fishSighting == null || item2.Value > num || (item2.Value == num && num3 < num2)) { fishSighting = dictionary2[item2.Key]; num = item2.Value; num2 = num3; } } return fishSighting; } } public struct Vec3 { public readonly float X; public readonly float Y; public readonly float Z; public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z); public float LengthXZ => (float)Math.Sqrt(X * X + Z * Z); public Vec3(float x, float y, float z) { X = x; Y = y; Z = z; } public static Vec3 operator -(Vec3 a, Vec3 b) { return new Vec3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }