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 AchievementProgress v1.1.1
plugins\AchievementProgress.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using 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 AchievementProgress.Config; using AchievementProgress.Core; using AchievementProgress.UI; using BepInEx; 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: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("AchievementProgress")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("Valheim Achievement Progress")] [assembly: AssemblyTitle("AchievementProgress")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.1.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; } } } namespace AchievementProgress { [BepInPlugin("com.pedro.achievementprogress", "AchievementProgress", "1.1.1")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "com.pedro.achievementprogress"; public const string PluginName = "AchievementProgress"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Logger; private Harmony _harmony; private void Awake() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); if (!ModConfig.Enabled.Value) { Logger.LogInfo((object)"[AchievementProgress] Deshabilitado en config."); return; } GameObject val = new GameObject("AchievementProgressManager"); Object.DontDestroyOnLoad((Object)val); val.AddComponent<ProgressWatcher>(); val.AddComponent<AchievementTracker>(); _harmony = new Harmony("com.pedro.achievementprogress"); _harmony.PatchAll(); Logger.LogInfo((object)"[AchievementProgress] v1.1.1 cargado (solo cliente)."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static void LogInfo(string message) { ManualLogSource logger = Logger; if (logger != null) { logger.LogInfo((object)message); } } public static void LogDebug(string message) { if (Logger != null && ModConfig.EnableDebugLogs != null && ModConfig.EnableDebugLogs.Value) { Logger.LogInfo((object)("[DEBUG] " + message)); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "com.pedro.achievementprogress"; public const string PLUGIN_NAME = "AchievementProgress"; public const string PLUGIN_VERSION = "1.1.1"; } } namespace AchievementProgress.UI { public sealed class AchievementTracker : MonoBehaviour { private struct PinBlock { public string Id; public string Header; public string Goals; } private const int MaxGoalRowsPerPin = 8; private const float RowIconSize = 32f; private const float HeaderBtn = 22f; private const float UnpinSize = 22f; private const float MaxPanelHeight = 420f; private GUIStyle _panelStyle; private GUIStyle _innerStyle; private GUIStyle _titleStyle; private GUIStyle _labelStyle; private GUIStyle _hintStyle; private GUIStyle _buttonStyle; private GUIStyle _iconButtonStyle; private GUIStyle _unpinButtonStyle; private GUIStyle _pickerRowStyle; private Texture2D _panelBg; private Texture2D _innerBg; private Texture2D _accentBg; private Texture2D _buttonBg; private Texture2D _buttonHoverBg; private Texture2D _dangerBg; private Texture2D _rowBg; private Texture2D _rowAltBg; private string _cachedBody = ""; private float _nextRefresh; private bool _dragging; private bool _dragMoved; private Vector2 _dragOffset; private float _runtimePosX; private float _runtimePosY; private bool _hasRuntimePos; private bool _pickerOpen; private Vector2 _pickerScroll; private Vector2 _bodyScroll; private string _statusMessage = ""; private float _statusExpire; private readonly List<IncompleteGoal> _goalBuffer = new List<IncompleteGoal>(16); private readonly List<TrackedAchievement> _pickerList = new List<TrackedAchievement>(128); private readonly List<PinBlock> _pinBlocks = new List<PinBlock>(3); private void Update() { if (!ModConfig.Enabled.Value || !ModConfig.EnableDirectionalIndicator.Value) { _cachedBody = ""; _pinBlocks.Clear(); return; } if ((Object)(object)Player.m_localPlayer == (Object)null || !AchievementCatalog.IsReady) { _cachedBody = ""; _pinBlocks.Clear(); return; } if ((Object)(object)Hud.instance != (Object)null && Hud.instance.m_userHidden) { _cachedBody = ""; return; } float num = (IsPauseMenuVisible() ? 0.12f : 0.35f); if (!(Time.unscaledTime < _nextRefresh)) { _nextRefresh = Time.unscaledTime + num; PinnedAchievements.PruneInvalid(); RebuildPinBlocks(); } } private void OnGUI() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.EnableDirectionalIndicator.Value || (Object)(object)Player.m_localPlayer == (Object)null) { return; } bool flag = IsPauseMenuVisible(); if (ModConfig.IndicatorAutoHideOverlays.Value && IsBlockingGameUiVisible(excludePauseMenu: true)) { _pickerOpen = false; return; } if (!flag) { _pickerOpen = false; } if (PinnedAchievements.Ids.Count <= 0 && !flag) { return; } Event current = Event.current; if (!flag && (int)current.type != 7 && (int)current.type != 8) { return; } EnsureStyles(); float value = ModConfig.IndicatorWidth.Value; float num = (_pickerOpen ? 420f : Mathf.Clamp(EstimateContentHeight(flag, value), 72f, 420f)); ResolvePosition(value, num, out var posX, out var posY); float num2 = (flag ? 34f : 0f); Rect val = default(Rect); Rect val2 = default(Rect); Rect val3 = default(Rect); if (flag) { float num3 = posY + num - 22f - 6f; ((Rect)(ref val))..ctor(posX + 10f, num3, 22f, 22f); if (_pickerOpen) { ((Rect)(ref val3))..ctor(posX + 10f + 22f + 6f, num3, 72f, 22f); ((Rect)(ref val2))..ctor(posX + 10f + 22f + 84f, num3, value - 22f - 100f, 22f); } else { ((Rect)(ref val2))..ctor(posX + 10f + 22f + 6f, num3, value - 22f - 22f, 22f); } } HandleDragging(flag, ref posX, ref posY, value, num, val, val2, val3); if (flag) { float num4 = posY + num - 22f - 6f; ((Rect)(ref val))..ctor(posX + 10f, num4, 22f, 22f); if (_pickerOpen) { ((Rect)(ref val3))..ctor(posX + 10f + 22f + 6f, num4, 72f, 22f); ((Rect)(ref val2))..ctor(posX + 10f + 22f + 84f, num4, value - 22f - 100f, 22f); } else { ((Rect)(ref val2))..ctor(posX + 10f + 22f + 6f, num4, value - 22f - 22f, 22f); } } Rect rect = default(Rect); ((Rect)(ref rect))..ctor(posX, posY, value, num); DrawPanel(rect); GUI.Label(new Rect(posX + 12f, posY + 8f, value - 24f, 18f), _pickerOpen ? "<b>Elegir logro</b>" : "<b>Seguimiento</b>", _titleStyle); GUI.DrawTexture(new Rect(posX + 10f, posY + 28f, value - 20f, 2f), (Texture)(object)_accentBg); Rect contentRect = default(Rect); ((Rect)(ref contentRect))..ctor(posX + 8f, posY + 36f, value - 16f, num - 36f - num2 - 8f); if (_pickerOpen && flag) { DrawPickerInside(contentRect); } else { DrawPinnedContent(contentRect, flag); } if (flag) { bool flag2 = PinnedAchievements.CanPinMore(); Color color = GUI.color; if (!flag2 && !_pickerOpen) { GUI.color = new Color(1f, 1f, 1f, 0.4f); } string text = (_pickerOpen ? "←" : "+"); if (GUI.Button(val, text, _iconButtonStyle)) { if (_pickerOpen) { _pickerOpen = false; } else if (flag2) { _pickerOpen = true; _pickerScroll = Vector2.zero; RebuildPickerList(); } else { ShowStatus("Máximo 3 logros"); } } GUI.color = color; if (_pickerOpen && GUI.Button(val3, "Cerrar", _buttonStyle)) { _pickerOpen = false; } if (GUI.Button(val2, "Restablecer posición", _buttonStyle)) { ResetToAnchor(); } } if (!string.IsNullOrEmpty(_statusMessage) && Time.unscaledTime < _statusExpire) { GUI.Label(new Rect(posX + 10f, posY + num + 2f, value - 20f, 16f), "<color=#f0c987>" + _statusMessage + "</color>", _hintStyle); } } private float EstimateContentHeight(bool pauseMenuOpen, float width) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) float num = 44f; if (_pinBlocks.Count == 0) { num += 40f; } else { foreach (PinBlock pinBlock in _pinBlocks) { num += 26f; Vector2 val = _labelStyle.CalcSize(new GUIContent(pinBlock.Goals)); num += Mathf.Min(val.y + 8f, 120f); num += 8f; } } if (pauseMenuOpen) { num += 40f; } return num; } private void DrawPinnedContent(Rect contentRect, bool pauseMenuOpen) { //IL_000d: 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) //IL_0069: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_0109: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) if (_pinBlocks.Count == 0) { GUI.Label(contentRect, "<color=#cbb892>Ningún logro fijado</color>\n<color=#8f7e5c>Pulsa + para elegir (máx. 3)</color>", _labelStyle); return; } float num = 4f; foreach (PinBlock pinBlock2 in _pinBlocks) { num += 26f; num += _labelStyle.CalcHeight(new GUIContent(pinBlock2.Goals), ((Rect)(ref contentRect)).width - 8f) + 10f; } Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref contentRect)).width - 16f, Mathf.Max(((Rect)(ref contentRect)).height, num)); _bodyScroll = GUI.BeginScrollView(contentRect, _bodyScroll, val); float num2 = 2f; Rect row = default(Rect); for (int i = 0; i < _pinBlocks.Count; i++) { PinBlock pinBlock = _pinBlocks[i]; float num3 = _labelStyle.CalcHeight(new GUIContent(pinBlock.Goals), ((Rect)(ref val)).width - 4f); ((Rect)(ref row))..ctor(2f, num2, ((Rect)(ref val)).width - 4f, 24f); DrawRowBg(row, i % 2 == 0); GUI.Label(new Rect(((Rect)(ref row)).x + 6f, ((Rect)(ref row)).y + 2f, ((Rect)(ref row)).width - 22f - 12f, 20f), pinBlock.Header, _labelStyle); if (pauseMenuOpen && GUI.Button(new Rect(((Rect)(ref row)).xMax - 22f - 2f, ((Rect)(ref row)).y + 1f, 22f, 22f), "×", _unpinButtonStyle)) { PinnedAchievements.Unpin(pinBlock.Id); RebuildPinBlocks(); ShowStatus("Logro quitado"); } num2 += 26f; GUI.Label(new Rect(8f, num2, ((Rect)(ref val)).width - 12f, num3), pinBlock.Goals, _labelStyle); num2 += num3 + 10f; } GUI.EndScrollView(); } private void DrawPickerInside(Rect contentRect) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) float num = 44f; float num2 = Mathf.Max(((Rect)(ref contentRect)).height, (float)_pickerList.Count * num + 4f); Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref contentRect)).width - 16f, num2); _pickerScroll = GUI.BeginScrollView(contentRect, _pickerScroll, val); Rect row = default(Rect); for (int i = 0; i < _pickerList.Count; i++) { TrackedAchievement trackedAchievement = _pickerList[i]; if ((Object)(object)trackedAchievement?.Source == (Object)null) { continue; } ((Rect)(ref row))..ctor(2f, (float)i * num, ((Rect)(ref val)).width - 4f, num - 3f); DrawRowBg(row, i % 2 == 0); Sprite icon = AchievementCatalog.GetIcon(trackedAchievement.Source); DrawSprite(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y + 6f, 32f, 32f), icon); string text = AchievementCatalog.LocalizeNameForTracker(trackedAchievement.Source); FormatPickerProgress(trackedAchievement, out var statusLine, out var complete, out var canPin); GUI.Label(new Rect(((Rect)(ref row)).x + 42f, ((Rect)(ref row)).y + 3f, ((Rect)(ref row)).width - 110f, num - 6f), "<b><color=#f5ecd7>" + text + "</color></b>\n" + statusLine, _pickerRowStyle); if (canPin) { if (GUI.Button(new Rect(((Rect)(ref row)).xMax - 58f, ((Rect)(ref row)).y + 10f, 54f, 24f), "Fijar", _buttonStyle)) { if (PinnedAchievements.Pin(trackedAchievement.Id)) { _pickerOpen = false; RebuildPinBlocks(); ShowStatus("Logro fijado"); } else { ShowStatus("Máximo 3 logros"); } } } else if (complete) { GUI.Label(new Rect(((Rect)(ref row)).xMax - 70f, ((Rect)(ref row)).y + 12f, 66f, 20f), "<color=#8fca6e>Hecho</color>", _hintStyle); } } GUI.EndScrollView(); } private static void FormatPickerProgress(TrackedAchievement tracked, out string statusLine, out bool complete, out bool canPin) { complete = (Object)(object)tracked.Source != (Object)null && tracked.Source.m_unlocked; canPin = !complete && !PinnedAchievements.IsPinned(tracked.Id); if (complete) { statusLine = "<color=#8fca6e>Completado</color>"; return; } ProgressReader.GetProgress(tracked, out var current, out var total, out var completedParts, out var totalParts); if (totalParts > 1) { int num = Mathf.Max(0, totalParts - completedParts); if (num <= 0) { statusLine = $"<color=#e8c878>{completedParts}/{totalParts}</color> <color=#8fca6e>casi listo</color>"; } else { statusLine = $"<color=#e8c878>{completedParts}/{totalParts}</color> <color=#c9a27a>faltan {num}</color>"; } } else if (total > 0.01f) { float num2 = Mathf.Max(0f, total - current); if (num2 <= 0.01f) { statusLine = "<color=#e8c878>" + ProgressReader.FormatAmount(current) + " / " + ProgressReader.FormatAmount(total) + "</color>"; } else { statusLine = "<color=#e8c878>" + ProgressReader.FormatAmount(current) + " / " + ProgressReader.FormatAmount(total) + "</color> <color=#c9a27a>faltan " + ProgressReader.FormatAmount(num2) + "</color>"; } } else { statusLine = "<color=#c9a27a>Sin progreso</color>"; } } private void RebuildPickerList() { _pickerList.Clear(); if (!AchievementCatalog.IsReady) { return; } foreach (TrackedAchievement item in AchievementCatalog.All) { if (!((Object)(object)item?.Source == (Object)null) && !ModConfig.IsAchievementDisabled(item.Id) && !PinnedAchievements.IsPinned(item.Id)) { _pickerList.Add(item); } } _pickerList.Sort(delegate(TrackedAchievement a, TrackedAchievement b) { bool flag = (Object)(object)a.Source != (Object)null && a.Source.m_unlocked; bool flag2 = (Object)(object)b.Source != (Object)null && b.Source.m_unlocked; return (flag != flag2) ? (flag ? 1 : (-1)) : string.Compare(AchievementCatalog.LocalizeNameForTracker(a.Source), AchievementCatalog.LocalizeNameForTracker(b.Source), StringComparison.OrdinalIgnoreCase); }); } private void RebuildPinBlocks() { //IL_002c: 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) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) _pinBlocks.Clear(); _cachedBody = ""; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Vector3 position = ((Component)localPlayer).transform.position; StringBuilder stringBuilder = new StringBuilder(); foreach (TrackedAchievement item in PinnedAchievements.GetPinnedTracked()) { if ((Object)(object)item.Source == (Object)null || item.Source.m_unlocked) { continue; } ProgressReader.GetProgress(item, out var _, out var _, out var completedParts, out var totalParts); string text = AchievementCatalog.LocalizeNameForTracker(item.Source); string text2 = "<color=#f5ecd7><b>" + text + "</b></color>"; if (totalParts > 0) { text2 += $" <color=#e8c878>({completedParts}/{totalParts})</color>"; } ProgressReader.GetIncompleteGoals(item, position, _goalBuffer); int num = ProgressReader.CountIncompleteGoals(item); StringBuilder stringBuilder2 = new StringBuilder(); if (_goalBuffer.Count == 0) { stringBuilder2.Append("<color=#8f7e5c>Sin objetivos pendientes</color>"); } else { for (int i = 0; i < _goalBuffer.Count; i++) { IncompleteGoal incompleteGoal = _goalBuffer[i]; if (incompleteGoal.RichLine) { stringBuilder2.Append(incompleteGoal.Label); } else { stringBuilder2.Append("<color=#dcc9a0>• ").Append(incompleteGoal.Label).Append("</color>"); if (incompleteGoal.Total > 1.01f || incompleteGoal.Current > 0.01f) { stringBuilder2.Append(" <color=#a8926a>").Append(ProgressReader.FormatAmount(incompleteGoal.Current)).Append('/') .Append(ProgressReader.FormatAmount(incompleteGoal.Total)) .Append("</color>"); } } if (i < _goalBuffer.Count - 1) { stringBuilder2.Append('\n'); } } if (num > _goalBuffer.Count) { stringBuilder2.Append("\n<color=#8f7e5c>…y ").Append(num - _goalBuffer.Count).Append(" más</color>"); } } _pinBlocks.Add(new PinBlock { Id = item.Id, Header = text2, Goals = stringBuilder2.ToString() }); if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(text2).Append('\n').Append((object?)stringBuilder2); } _cachedBody = stringBuilder.ToString(); } private void ShowStatus(string msg) { _statusMessage = msg; _statusExpire = Time.unscaledTime + 2.5f; } private void DrawPanel(Rect rect) { //IL_0034: 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_0089: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect(((Rect)(ref rect)).x - 2f, ((Rect)(ref rect)).y - 2f, ((Rect)(ref rect)).width + 4f, ((Rect)(ref rect)).height + 4f), (Texture)(object)_accentBg); GUI.Box(rect, GUIContent.none, _panelStyle); GUI.Box(new Rect(((Rect)(ref rect)).x + 3f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width - 6f, ((Rect)(ref rect)).height - 6f), GUIContent.none, _innerStyle); } private void DrawRowBg(Rect row, bool alt) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(row, (Texture)(object)(alt ? _rowAltBg : _rowBg)); } private static void DrawSprite(Rect rect, Sprite sprite) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sprite == (Object)null || (Object)(object)sprite.texture == (Object)null) { Color color = GUI.color; GUI.color = new Color(0.45f, 0.35f, 0.2f, 0.7f); GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color; } else { Rect textureRect = sprite.textureRect; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)sprite.texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)sprite.texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)sprite.texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)sprite.texture).height); GUI.DrawTextureWithTexCoords(rect, (Texture)(object)sprite.texture, val); } } private void EnsureStyles() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Expected O, but got Unknown //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Expected O, but got Unknown //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Expected O, but got Unknown //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Expected O, but got Unknown //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Expected O, but got Unknown //IL_0448: Unknown result type (might be due to invalid IL or missing references) if (_panelStyle == null) { _panelBg = MakeTex(new Color(0.12f, 0.1f, 0.08f, 0.94f)); _innerBg = MakeTex(new Color(0.22f, 0.17f, 0.11f, 0.55f)); _accentBg = MakeTex(new Color(0.85f, 0.65f, 0.28f, 0.95f)); _buttonBg = MakeTex(new Color(0.42f, 0.3f, 0.14f, 0.98f)); _buttonHoverBg = MakeTex(new Color(0.58f, 0.42f, 0.18f, 1f)); _dangerBg = MakeTex(new Color(0.55f, 0.22f, 0.16f, 0.95f)); _rowBg = MakeTex(new Color(0.28f, 0.22f, 0.14f, 0.65f)); _rowAltBg = MakeTex(new Color(0.32f, 0.25f, 0.16f, 0.55f)); GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = _panelBg; _panelStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.box); val2.normal.background = _innerBg; _innerStyle = val2; _titleStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)0, fontSize = 14, richText = true, fontStyle = (FontStyle)1 }; _titleStyle.normal.textColor = new Color(0.96f, 0.9f, 0.72f, 1f); _labelStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)0, fontSize = 12, richText = true, wordWrap = true }; _labelStyle.normal.textColor = new Color(0.96f, 0.93f, 0.84f, 1f); _hintStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)5, fontSize = 11, richText = true }; _hintStyle.normal.textColor = new Color(0.85f, 0.75f, 0.55f, 1f); GUIStyle val3 = new GUIStyle(GUI.skin.button) { fontSize = 11, alignment = (TextAnchor)4, richText = true, padding = new RectOffset(4, 4, 2, 2) }; val3.normal.background = _buttonBg; val3.normal.textColor = new Color(0.98f, 0.93f, 0.78f); val3.hover.background = _buttonHoverBg; val3.hover.textColor = Color.white; val3.active.background = _buttonHoverBg; val3.active.textColor = Color.white; _buttonStyle = val3; _iconButtonStyle = new GUIStyle(_buttonStyle) { fontSize = 16, fontStyle = (FontStyle)1 }; Texture2D background = MakeTex(new Color(0.72f, 0.3f, 0.2f, 1f)); GUIStyle val4 = new GUIStyle(_buttonStyle) { fontSize = 14, fontStyle = (FontStyle)1 }; val4.normal.background = _dangerBg; val4.normal.textColor = new Color(1f, 0.92f, 0.85f); val4.hover.background = background; val4.hover.textColor = Color.white; val4.active.background = _dangerBg; val4.active.textColor = Color.white; _unpinButtonStyle = val4; _pickerRowStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)3, fontSize = 11, richText = true, wordWrap = true }; _pickerRowStyle.normal.textColor = new Color(0.96f, 0.93f, 0.84f, 1f); } } private static Texture2D MakeTex(Color c) { //IL_0004: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0021: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, c); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; return val; } private void HandleDragging(bool pauseMenuOpen, ref float posX, ref float posY, float width, float height, Rect ignoreA, Rect ignoreB, Rect ignoreC) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Invalid comparison between Unknown and I4 //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Invalid comparison between Unknown and I4 //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: 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) if (!pauseMenuOpen) { if (_dragging) { EndDrag(posX, posY, width, height); } } else { if (_pickerOpen) { return; } Event current = Event.current; Rect val = default(Rect); ((Rect)(ref val))..ctor(posX, posY, width, height); if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref val)).Contains(current.mousePosition)) { if ((((Rect)(ref ignoreA)).width > 1f && ((Rect)(ref ignoreA)).Contains(current.mousePosition)) || (((Rect)(ref ignoreB)).width > 1f && ((Rect)(ref ignoreB)).Contains(current.mousePosition)) || (((Rect)(ref ignoreC)).width > 1f && ((Rect)(ref ignoreC)).Contains(current.mousePosition))) { return; } _dragging = true; _dragMoved = false; _dragOffset = current.mousePosition - new Vector2(posX, posY); current.Use(); } if (_dragging && (int)current.type == 3) { Vector2 val2 = current.mousePosition - _dragOffset; if (Vector2.Distance(val2, new Vector2(posX, posY)) > 1f) { _dragMoved = true; } posX = val2.x; posY = val2.y; ClampPosition(ref posX, ref posY, width, height); _runtimePosX = posX; _runtimePosY = posY; _hasRuntimePos = true; current.Use(); } if (_dragging && (int)current.type == 1) { EndDrag(posX, posY, width, height); current.Use(); } } } private void EndDrag(float posX, float posY, float width, float height) { _dragging = false; ClampPosition(ref posX, ref posY, width, height); if (_dragMoved) { _runtimePosX = posX; _runtimePosY = posY; _hasRuntimePos = true; ModConfig.IndicatorUseCustomPosition.Value = true; ModConfig.IndicatorCustomNormX.Value = ((Screen.width > 0) ? (posX / (float)Screen.width) : 0f); ModConfig.IndicatorCustomNormY.Value = ((Screen.height > 0) ? (posY / (float)Screen.height) : 0f); ModConfig.Save(); } } private void ResetToAnchor() { ModConfig.IndicatorUseCustomPosition.Value = false; ModConfig.IndicatorCustomNormX.Value = 0f; ModConfig.IndicatorCustomNormY.Value = 0f; _dragging = false; _dragMoved = false; _hasRuntimePos = false; ModConfig.Save(); } private void ResolvePosition(float width, float height, out float posX, out float posY) { if (_hasRuntimePos && (_dragging || ModConfig.IndicatorUseCustomPosition.Value)) { posX = _runtimePosX; posY = _runtimePosY; ClampPosition(ref posX, ref posY, width, height); return; } if (ModConfig.IndicatorUseCustomPosition.Value) { posX = Mathf.Clamp01(ModConfig.IndicatorCustomNormX.Value) * (float)Screen.width; posY = Mathf.Clamp01(ModConfig.IndicatorCustomNormY.Value) * (float)Screen.height; ClampPosition(ref posX, ref posY, width, height); _runtimePosX = posX; _runtimePosY = posY; _hasRuntimePos = true; return; } float value = ModConfig.IndicatorOffsetX.Value; float value2 = ModConfig.IndicatorOffsetY.Value; switch ((ModConfig.IndicatorAnchor.Value ?? "BelowHotbar").Trim().ToLowerInvariant()) { case "belowhotbar": case "hotbar": case "below_equipment": { if (TryGetHotkeyBarScreenRect(out var imguiRect)) { posX = ((Rect)(ref imguiRect)).x + (((Rect)(ref imguiRect)).width - width) * 0.5f + value; posY = ((Rect)(ref imguiRect)).yMax + value2; ClampPosition(ref posX, ref posY, width, height); } else { float num = Mathf.Clamp((float)Screen.height * 0.07f, 48f, 96f); float num2 = Mathf.Clamp((float)Screen.height * 0.02f, 8f, 40f); posX = ((float)Screen.width - width) * 0.5f + value; posY = num2 + num + value2; ClampPosition(ref posX, ref posY, width, height); } return; } case "topleft": posX = value; posY = value2; break; case "bottomleft": posX = value; posY = (float)Screen.height - height - value2; break; case "bottomright": posX = (float)Screen.width - width - value; posY = (float)Screen.height - height - value2; break; case "topright": posX = (float)Screen.width - width - value; posY = value2; break; default: posX = ((float)Screen.width - width) * 0.5f + value; posY = (float)Screen.height * 0.1f + value2; break; } ClampPosition(ref posX, ref posY, width, height); } private static bool TryGetHotkeyBarScreenRect(out Rect imguiRect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) imguiRect = default(Rect); try { RectTransform val = null; HotkeyBar val2 = Object.FindObjectOfType<HotkeyBar>(); if ((Object)(object)val2 != (Object)null && ((Behaviour)val2).isActiveAndEnabled) { Transform transform = ((Component)val2).transform; val = (RectTransform)(((object)((transform is RectTransform) ? transform : null)) ?? ((object)((Component)val2).GetComponent<RectTransform>())); } if ((Object)(object)val == (Object)null && (Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_actionBarRoot != (Object)null) { GameObject actionBarRoot = Hud.instance.m_actionBarRoot; if (actionBarRoot.activeInHierarchy) { val = (RectTransform)(((object)actionBarRoot.GetComponent<RectTransform>()) ?? ((object)/*isinst with value type is only supported in some contexts*/)); } } if ((Object)(object)val == (Object)null) { return false; } Vector3[] array = (Vector3[])(object)new Vector3[4]; val.GetWorldCorners(array); float num = array[0].x; float num2 = array[0].x; float num3 = array[0].y; float num4 = array[0].y; for (int i = 1; i < 4; i++) { num = Mathf.Min(num, array[i].x); num2 = Mathf.Max(num2, array[i].x); num3 = Mathf.Min(num3, array[i].y); num4 = Mathf.Max(num4, array[i].y); } float num5 = (float)Screen.height - num4; float num6 = (float)Screen.height - num3; imguiRect = new Rect(num, num5, num2 - num, num6 - num5); return ((Rect)(ref imguiRect)).width > 8f && ((Rect)(ref imguiRect)).height > 4f; } catch { return false; } } private static void ClampPosition(ref float posX, ref float posY, float width, float height) { posX = Mathf.Clamp(posX, 8f, Mathf.Max(8f, (float)Screen.width - width - 8f)); posY = Mathf.Clamp(posY, 8f, Mathf.Max(8f, (float)Screen.height - height - 8f)); } private static bool IsPauseMenuVisible() { try { return Menu.IsVisible(); } catch { return false; } } private static bool IsBlockingGameUiVisible(bool excludePauseMenu) { try { if (InventoryGui.IsVisible()) { return true; } if (!excludePauseMenu && Menu.IsVisible()) { return true; } if (TextInput.IsVisible()) { return true; } if (StoreGui.IsVisible()) { return true; } if (Minimap.IsOpen()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } } catch { } return false; } } public static class ToastNotifier { public static void Show(TrackedAchievement tracked, float delta, float current, float total) { if ((Object)(object)tracked?.Source == (Object)null || !ModConfig.EnableNotifications.Value) { return; } string text = AchievementCatalog.LocalizeName(tracked.Source); Sprite icon = AchievementCatalog.GetIcon(tracked.Source); string text2 = BuildBody(delta, current, total); if ((Object)(object)MessageHud.instance != (Object)null) { try { UnlockToastGate.BannerText = (ModConfig.ProgressBannerText.Value ?? "Nuevo progreso de logro: {name}").Replace("{name}", text); UnlockToastGate.BannerExpireTime = Time.unscaledTime + 8f; UnlockToastGate.FromMod = true; try { MessageHud.instance.QueueUnlockMsg(icon, text, text2); } finally { UnlockToastGate.FromMod = false; } if (ModConfig.ReplaceUnlockLogBanner.Value && !string.IsNullOrEmpty(UnlockToastGate.BannerText)) { MessageHud.instance.ShowMessage((MessageType)1, UnlockToastGate.BannerText, 0, (Sprite)null, false, true); } Plugin.LogDebug("Toast: " + tracked.Id + " | " + text + " | " + text2); return; } catch (Exception ex) { Plugin.LogDebug("Toast falló (ignorado): " + ex.Message); return; } } Plugin.LogDebug("Toast (sin MessageHud): " + text + " " + text2); } private static string BuildBody(float delta, float current, float total) { int num = Mathf.Max(1, Mathf.RoundToInt(delta)); int num2 = Mathf.RoundToInt(current); int num3 = Mathf.RoundToInt(total); bool value = ModConfig.ShowPlusDelta.Value; bool value2 = ModConfig.ShowTotal.Value; if (value && value2) { return $"+{num} · {num2} / {num3}"; } if (value) { return $"+{num}"; } if (value2) { return $"{num2} / {num3}"; } return $"{num2}"; } } internal static class UnlockToastGate { public static bool FromMod; public static string BannerText; public static float BannerExpireTime; } } namespace AchievementProgress.Patches { [HarmonyPatch(typeof(AchievementsGui), "CreateStatRow")] internal static class AchievementsGui_CreateStatRow_Patch { private static readonly FieldInfo PrefabField = AccessTools.Field(typeof(AchievementsGui), "m_achievementDetailsElementPrefab"); private static readonly FieldInfo RootField = AccessTools.Field(typeof(AchievementsGui), "m_achievementDetailsListRoot"); private static bool Prefix(AchievementsGui __instance, string statKey, float currentAmount, float totalAmount) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || !ModConfig.RevealHiddenRequirements.Value) { return true; } try { object? obj = PrefabField?.GetValue(__instance); AchievementDetailUnlockCondition val = (AchievementDetailUnlockCondition)((obj is AchievementDetailUnlockCondition) ? obj : null); object? obj2 = RootField?.GetValue(__instance); Transform val2 = (Transform)((obj2 is Transform) ? obj2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return true; } AchievementDetailUnlockCondition val3 = Object.Instantiate<AchievementDetailUnlockCondition>(val, val2); ((Component)val3).gameObject.SetActive(true); AchievementDetailUnlockCondition val4 = ((Component)val3).GetComponent<AchievementDetailUnlockCondition>() ?? val3; string text = statKey; if (Enum.TryParse<PlayerStatType>(statKey, out PlayerStatType _)) { text = "$stat_" + statKey; } string text2 = text; try { if (Localization.instance != null) { text2 = Localization.instance.Localize(text); } } catch { } if (string.IsNullOrEmpty(text2)) { text2 = text; } Color color = (Color)((currentAmount >= totalAmount) ? Color.green : new Color(0.92f, 0.88f, 0.78f, 1f)); SetLabel((TMP_Text)(object)val4.StatName, text2, color); SetLabel((TMP_Text)(object)val4.Progress, FormatAmount(currentAmount) + " / " + FormatAmount(totalAmount), color); Plugin.LogDebug("Reveal row → " + text2 + " | " + FormatAmount(currentAmount) + " / " + FormatAmount(totalAmount)); return false; } catch (Exception ex) { Plugin.LogInfo("CreateStatRow reveal falló, usando vanilla: " + ex.Message); return true; } } private static void SetLabel(TMP_Text label, string text, Color color) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)label == (Object)null)) { label.text = text; ((Graphic)label).color = color; } } private static string FormatAmount(float value) { if (Mathf.Abs(value - Mathf.Round(value)) < 0.001f) { return Mathf.RoundToInt(value).ToString(); } return value.ToString("0.#"); } } [HarmonyPatch(typeof(AchievementsGui), "OnOpenAchievementDetails")] internal static class AchievementsGui_OnOpenAchievementDetails_Patch { private static void Prefix(Achievement achievement, ref bool __state) { __state = false; if (ModConfig.Enabled.Value && ModConfig.RevealHiddenRequirements.Value && !((Object)(object)achievement == (Object)null) && achievement.m_isSecret && !achievement.m_unlocked) { __state = true; achievement.m_isSecret = false; } } private static void Finalizer(Achievement achievement, bool __state) { if (__state && (Object)(object)achievement != (Object)null) { achievement.m_isSecret = true; } } } [HarmonyPatch(typeof(Achievements))] internal static class AchievementsPatches { [HarmonyPostfix] [HarmonyPatch("AchievementEvent")] private static void AchievementEvent_Postfix(Achievement ach, bool showPopup) { if (!((Object)(object)ach == (Object)null) && ach.m_unlocked) { ProgressWatcher.Instance?.OnAchievementUnlocked(ach); } } [HarmonyPostfix] [HarmonyPatch("Initialize")] private static void Initialize_Postfix() { AchievementCatalog.Rebuild(); ProgressWatcher.Instance?.ResetSession(); ProgressWatcher.Instance?.SeedProgressBaselines(); Plugin.LogInfo("Achievements.Initialize → catálogo reconstruido."); } } [HarmonyPatch(typeof(Game))] internal static class GamePatches { [HarmonyPostfix] [HarmonyPatch("SpawnPlayer")] private static void SpawnPlayer_Postfix() { if (!AchievementCatalog.IsReady) { AchievementCatalog.Rebuild(); } ProgressWatcher.Instance?.SeedProgressBaselines(); } } [HarmonyPatch(typeof(MessageHud))] internal static class MessageHudPatches { private static readonly FieldInfo UnlockQueueField = AccessTools.Field(typeof(MessageHud), "m_unlockMsgQueue"); private static readonly Type UnlockMsgType = AccessTools.Inner(typeof(MessageHud), "UnlockMsg"); [HarmonyPrefix] [HarmonyPatch("QueueUnlockMsg")] private static bool QueueUnlockMsg_Prefix(MessageHud __instance, Sprite icon, string topic, string description) { if (!UnlockToastGate.FromMod) { return true; } try { object obj = UnlockQueueField?.GetValue(__instance); if (obj == null || UnlockMsgType == null) { return true; } object obj2 = Activator.CreateInstance(UnlockMsgType); UnlockMsgType.GetField("m_icon")?.SetValue(obj2, icon); string text = topic; string value = description; try { if (Localization.instance != null) { text = Localization.instance.Localize(topic); value = Localization.instance.Localize(description); } } catch { } UnlockMsgType.GetField("m_topic")?.SetValue(obj2, text); UnlockMsgType.GetField("m_description")?.SetValue(obj2, value); MethodInfo methodInfo = obj.GetType().GetMethod("Enqueue", new Type[1] { UnlockMsgType }) ?? obj.GetType().GetMethod("Enqueue"); if (methodInfo == null) { return true; } methodInfo.Invoke(obj, new object[1] { obj2 }); Plugin.LogDebug("Unlock toast encolado sin AddLog: " + text); return false; } catch (Exception ex) { Plugin.LogInfo("QueueUnlockMsg custom falló, usando vanilla: " + ex.Message); return true; } } [HarmonyPrefix] [HarmonyPatch("ShowMessage")] private static void ShowMessage_Prefix(ref string text) { try { if (ModConfig.Enabled.Value && ModConfig.ReplaceUnlockLogBanner.Value && !string.IsNullOrEmpty(UnlockToastGate.BannerText)) { if (Time.unscaledTime > UnlockToastGate.BannerExpireTime) { UnlockToastGate.BannerText = null; } else if (LooksLikeUnlockLogBanner(text)) { text = UnlockToastGate.BannerText; UnlockToastGate.BannerText = null; Plugin.LogDebug("Banner reemplazado → " + text); } } } catch (Exception ex) { Plugin.LogDebug("ShowMessage prefix falló (ignorado): " + ex.Message); } } private static bool LooksLikeUnlockLogBanner(string text) { if (string.IsNullOrEmpty(text)) { return false; } string text2 = text.ToLowerInvariant(); if (text2.Contains("registro de mensajes")) { return true; } if (text2.Contains("message log")) { return true; } if (text2.Contains("nuevos objetos")) { return true; } if (text2.Contains("new items unlocked")) { return true; } if (text2.Contains("new objects unlocked")) { return true; } if (text2.Contains("desbloqueados en el registro")) { return true; } if (text2.Contains("unlocked in the message")) { return true; } return false; } } [HarmonyPatch(typeof(PlayerProfile))] internal static class PlayerProfilePatches { [HarmonyPrefix] [HarmonyPatch("IncrementStat", new Type[] { typeof(PlayerStatType), typeof(float), typeof(bool) })] private static void IncrementStat_Prefix(PlayerStatType stat, out float __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) __state = SafeGetStat(stat); } [HarmonyPostfix] [HarmonyPatch("IncrementStat", new Type[] { typeof(PlayerStatType), typeof(float), typeof(bool) })] private static void IncrementStat_Postfix(PlayerStatType stat, float __state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SafeEnqueueStat(stat, __state); } [HarmonyPrefix] [HarmonyPatch("SetStat")] private static void SetStat_Prefix(PlayerStatType stat, out float __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) __state = SafeGetStat(stat); } [HarmonyPostfix] [HarmonyPatch("SetStat")] private static void SetStat_Postfix(PlayerStatType stat, float __state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SafeEnqueueStat(stat, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatEnemy")] private static void IncrementStatEnemy_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.Enemy, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatEnemy")] private static void IncrementStatEnemy_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.Enemy, name, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatItemPickup")] private static void IncrementStatItemPickup_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.ItemPickup, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatItemPickup")] private static void IncrementStatItemPickup_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.ItemPickup, name, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatItemCraft")] private static void IncrementStatItemCraft_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.ItemCraft, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatItemCraft")] private static void IncrementStatItemCraft_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.ItemCraft, name, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatPickable")] private static void IncrementStatPickable_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.Pickable, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatPickable")] private static void IncrementStatPickable_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.Pickable, name, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatFoodEaten")] private static void IncrementStatFoodEaten_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.FoodEaten, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatFoodEaten")] private static void IncrementStatFoodEaten_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.FoodEaten, name, __state); } [HarmonyPrefix] [HarmonyPatch("IncrementStatBuildPiecePlaced")] private static void IncrementStatBuildPiecePlaced_Prefix(string name, out float __state) { __state = SafeGetDict(TrackedAchievement.DictChannel.PiecePlaced, name); } [HarmonyPostfix] [HarmonyPatch("IncrementStatBuildPiecePlaced")] private static void IncrementStatBuildPiecePlaced_Postfix(string name, float __state) { SafeEnqueueDict(TrackedAchievement.DictChannel.PiecePlaced, name, __state); } private static float SafeGetStat(PlayerStatType stat) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return ProgressReader.GetStat(stat); } catch { return 0f; } } private static float SafeGetDict(TrackedAchievement.DictChannel channel, string name) { try { return ProgressReader.GetDictAmount(channel, name); } catch { return 0f; } } private static void SafeEnqueueStat(PlayerStatType stat, float before) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) try { float stat2 = ProgressReader.GetStat(stat); ProgressWatcher.Instance?.EnqueueStatChange(stat, before, stat2); } catch (Exception ex) { Plugin.LogDebug("IncrementStat observe failed: " + ex.Message); } } private static void SafeEnqueueDict(TrackedAchievement.DictChannel channel, string name, float before) { try { float dictAmount = ProgressReader.GetDictAmount(channel, name); ProgressWatcher.Instance?.EnqueueDictChange(channel, name, before, dictAmount); } catch (Exception ex) { Plugin.LogDebug("DictStat observe failed: " + ex.Message); } } } } namespace AchievementProgress.Core { public static class AchievementCatalog { private static readonly HashSet<string> SpamIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "GrindTrees", "GrindBuild", "GrindHarvest", "GrindHoney", "GrindEat", "GrindCook", "GrindForager", "GrindSail", "GrindSailHelm", "KillAllCreatures", "KillAllCreaturesHard" }; private static readonly HashSet<string> DirectionalIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "ExploreNSEW", "ExploreNSEWNoMap" }; private static readonly List<TrackedAchievement> _all = new List<TrackedAchievement>(); private static readonly Dictionary<PlayerStatType, List<TrackedAchievement>> _byStat = new Dictionary<PlayerStatType, List<TrackedAchievement>>(); private static readonly Dictionary<string, List<TrackedAchievement>> _byDictKey = new Dictionary<string, List<TrackedAchievement>>(StringComparer.OrdinalIgnoreCase); private static readonly List<TrackedAchievement> _meta = new List<TrackedAchievement>(); private static readonly List<TrackedAchievement> _directional = new List<TrackedAchievement>(); public static bool IsReady { get; private set; } public static IReadOnlyList<TrackedAchievement> All => _all; public static IReadOnlyList<TrackedAchievement> Directional => _directional; public static void Rebuild() { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) _all.Clear(); _byStat.Clear(); _byDictKey.Clear(); _meta.Clear(); _directional.Clear(); IsReady = false; if ((Object)(object)Achievements.m_instance == (Object)null) { Plugin.LogDebug("Catalog: Achievements.m_instance es null."); return; } foreach (AchievementList achievementList in Achievements.m_instance.m_achievementLists) { if (achievementList?.m_achievements == null) { continue; } foreach (Achievement achievement in achievementList.m_achievements) { if ((Object)(object)achievement == (Object)null || string.IsNullOrEmpty(achievement.m_id)) { continue; } TrackedAchievement trackedAchievement = BuildTracked(achievement); _all.Add(trackedAchievement); if (DirectionalIds.Contains(trackedAchievement.Id)) { _directional.Add(trackedAchievement); } if (trackedAchievement.Kind == ProgressKind.Meta) { _meta.Add(trackedAchievement); } foreach (TrackedAchievement.StatGoal statGoal in trackedAchievement.StatGoals) { if (!_byStat.TryGetValue(statGoal.Stat, out var value)) { value = new List<TrackedAchievement>(); _byStat[statGoal.Stat] = value; } if (!value.Contains(trackedAchievement)) { value.Add(trackedAchievement); } } foreach (TrackedAchievement.DictGoal dictGoal in trackedAchievement.DictGoals) { string key = DictMapKey(dictGoal.Channel, dictGoal.Key); if (!_byDictKey.TryGetValue(key, out var value2)) { value2 = new List<TrackedAchievement>(); _byDictKey[key] = value2; } if (!value2.Contains(trackedAchievement)) { value2.Add(trackedAchievement); } } } } IsReady = _all.Count > 0; Plugin.LogInfo($"Catalog: {_all.Count} logros indexados " + $"(counters={_all.Count((TrackedAchievement t) => t.Kind == ProgressKind.Counter)}, " + $"collections={_all.Count((TrackedAchievement t) => t.Kind == ProgressKind.Collection)}, " + $"meta={_meta.Count}, binary={_all.Count((TrackedAchievement t) => t.Kind == ProgressKind.Binary)})."); } public static bool TryGetByStat(PlayerStatType stat, out List<TrackedAchievement> list) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return _byStat.TryGetValue(stat, out list); } public static bool TryGetByDict(TrackedAchievement.DictChannel channel, string key, out List<TrackedAchievement> list) { return _byDictKey.TryGetValue(DictMapKey(channel, key), out list); } public static bool TryGetById(string id, out TrackedAchievement tracked) { tracked = null; if (string.IsNullOrEmpty(id)) { return false; } for (int i = 0; i < _all.Count; i++) { if (string.Equals(_all[i].Id, id, StringComparison.OrdinalIgnoreCase)) { tracked = _all[i]; return true; } } return false; } public static bool IsDirectional(string id) { if (!string.IsNullOrEmpty(id)) { return DirectionalIds.Contains(id); } return false; } public static IEnumerable<TrackedAchievement> MetaAchievements() { return _meta; } private static string DictMapKey(TrackedAchievement.DictChannel channel, string key) { return $"{channel}:{key}"; } private static TrackedAchievement BuildTracked(Achievement ach) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) TrackedAchievement trackedAchievement = new TrackedAchievement { Source = ach, Id = ach.m_id, UseSpamCooldown = SpamIds.Contains(ach.m_id) }; if (ach.m_statTrigger != null) { foreach (PlayerStatRequirement item in ach.m_statTrigger) { trackedAchievement.StatGoals.Add(new TrackedAchievement.StatGoal { Stat = item.m_stat, Target = item.m_amountAboveEquals }); } } AddDict(trackedAchievement, TrackedAchievement.DictChannel.Enemy, ach.m_enemyStatsTriggers); AddDict(trackedAchievement, TrackedAchievement.DictChannel.ItemPickup, ach.m_itemPickupTriggers); AddDict(trackedAchievement, TrackedAchievement.DictChannel.ItemCraft, ach.m_itemCraftTriggers); AddDict(trackedAchievement, TrackedAchievement.DictChannel.FoodEaten, ach.m_foodEatenTriggers); AddDict(trackedAchievement, TrackedAchievement.DictChannel.Pickable, ach.m_pickableTriggers); AddDict(trackedAchievement, TrackedAchievement.DictChannel.PiecePlaced, ach.m_piecePlacedTriggers); if (ach.m_otherAchievementTriggers != null) { foreach (Achievement otherAchievementTrigger in ach.m_otherAchievementTriggers) { if ((Object)(object)otherAchievementTrigger != (Object)null) { trackedAchievement.MetaGoals.Add(otherAchievementTrigger); } } } trackedAchievement.Kind = Classify(trackedAchievement); if ((trackedAchievement.Kind == ProgressKind.Collection || trackedAchievement.Kind == ProgressKind.Counter) && (SpamIds.Contains(trackedAchievement.Id) || trackedAchievement.Kind == ProgressKind.Collection)) { trackedAchievement.UseSpamCooldown = SpamIds.Contains(trackedAchievement.Id) || trackedAchievement.DictGoals.Count > 8; } return trackedAchievement; } private static void AddDict(TrackedAchievement tracked, TrackedAchievement.DictChannel channel, List<EnemyStatRequirement> reqs) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (reqs == null) { return; } foreach (EnemyStatRequirement req in reqs) { if (!string.IsNullOrEmpty(req.m_stat)) { tracked.DictGoals.Add(new TrackedAchievement.DictGoal { Channel = channel, Key = req.m_stat, Target = Mathf.Max(1f, req.m_amount) }); } } } private static void AddDict(TrackedAchievement tracked, TrackedAchievement.DictChannel channel, List<DictStatRequirement> reqs) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (reqs == null) { return; } foreach (DictStatRequirement req in reqs) { if (!string.IsNullOrEmpty(req.m_stat)) { tracked.DictGoals.Add(new TrackedAchievement.DictGoal { Channel = channel, Key = req.m_stat, Target = Mathf.Max(1f, req.m_amount) }); } } } private static ProgressKind Classify(TrackedAchievement t) { if (t.MetaGoals.Count > 0 && t.StatGoals.Count == 0 && t.DictGoals.Count == 0) { return ProgressKind.Meta; } if (t.DictGoals.Count >= 2) { return ProgressKind.Collection; } if (t.StatGoals.Count >= 2 && t.StatGoals.All((TrackedAchievement.StatGoal g) => g.Target <= 1.01f) && t.DictGoals.Count == 0) { return ProgressKind.Collection; } if (t.StatGoals.Count >= 1 && t.StatGoals.Exists((TrackedAchievement.StatGoal g) => g.Target > 1.01f) && t.DictGoals.Count == 0) { return ProgressKind.Counter; } if (t.StatGoals.Count == 1 || t.DictGoals.Count == 1) { return ProgressKind.Binary; } if (t.DictGoals.Count > 0 || t.StatGoals.Count > 0) { return ProgressKind.Collection; } return ProgressKind.Binary; } public static string LocalizeName(Achievement ach) { if ((Object)(object)ach == (Object)null) { return "?"; } if (ach.m_isSecret && !ach.m_unlocked && !ModConfig.RevealSecretsInToasts.Value) { return "???"; } return LocalizeNameAlways(ach); } public static string LocalizeNameAlways(Achievement ach) { if ((Object)(object)ach == (Object)null) { return "?"; } try { if (Localization.instance != null) { return Localization.instance.Localize(ach.m_name); } } catch { } if (!string.IsNullOrEmpty(ach.m_name)) { return ach.m_name; } return ach.m_id; } public static Sprite GetIcon(Achievement ach) { if ((Object)(object)ach == (Object)null) { return null; } if (ach.m_unlocked || ModConfig.RevealSecretsInToasts.Value || !ach.m_isSecret || ModConfig.RevealHiddenRequirements.Value) { if (!((Object)(object)ach.m_icon != (Object)null)) { return ach.m_iconLocked; } return ach.m_icon; } if (!((Object)(object)ach.m_iconLocked != (Object)null)) { return ach.m_icon; } return ach.m_iconLocked; } public static string LocalizeNameForTracker(Achievement ach) { if ((Object)(object)ach == (Object)null) { return "?"; } if (ModConfig.RevealHiddenRequirements.Value) { return LocalizeNameAlways(ach); } return LocalizeName(ach); } } internal static class DictKeyUtil { public static IEnumerable<string> Aliases(string key) { if (string.IsNullOrEmpty(key)) { yield break; } HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> list = new List<string>(); Add(key); if (key.StartsWith("$", StringComparison.Ordinal)) { Add(key.Substring(1)); } else { Add("$" + key); } Add(SafeLocalize(key)); if (key.StartsWith("$", StringComparison.Ordinal)) { Add(SafeLocalize(key.Substring(1))); } else { Add(SafeLocalize("$" + key)); } foreach (string item in list) { yield return item; } void Add(string value) { if (!string.IsNullOrEmpty(value) && seen.Add(value)) { list.Add(value); } } } public static bool TryGetValue(Dictionary<string, float> dict, string key, out float value) { value = 0f; if (dict == null || string.IsNullOrEmpty(key)) { return false; } if (dict.TryGetValue(key, out value)) { return true; } foreach (string item in Aliases(key)) { if (dict.TryGetValue(item, out value)) { return true; } } string text = SafeLocalize(key); if (string.IsNullOrEmpty(text)) { return false; } foreach (KeyValuePair<string, float> item2 in dict) { if (string.Equals(item2.Key, text, StringComparison.OrdinalIgnoreCase) || string.Equals(SafeLocalize(item2.Key), text, StringComparison.OrdinalIgnoreCase)) { value = item2.Value; return true; } } return false; } private static string SafeLocalize(string token) { if (string.IsNullOrEmpty(token)) { return token; } try { if (Localization.instance == null) { return token; } string text = Localization.instance.Localize(token.StartsWith("$", StringComparison.Ordinal) ? token : ("$" + token)); return string.IsNullOrEmpty(text) ? token : text; } catch { return token; } } } public static class DirectionalProgress { public const float ExploreThresholdMeters = 10350f; public static bool IsDirectionalAchievement(TrackedAchievement tracked) { if (tracked != null) { return AchievementCatalog.IsDirectional(tracked.Id); } return false; } public unsafe static string FormatIncompleteDirections(TrackedAchievement tracked, Vector3 pos) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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) if (tracked?.StatGoals == null || tracked.StatGoals.Count == 0) { return ""; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < tracked.StatGoals.Count; i++) { TrackedAchievement.StatGoal statGoal = tracked.StatGoals[i]; if (!(ProgressReader.GetStat(statGoal.Stat) >= statGoal.Target)) { string value = CardinalLabel(((object)(*(PlayerStatType*)(&statGoal.Stat))/*cast due to .constrained prefix*/).ToString()); int num = Mathf.RoundToInt(EstimateProgressToward(statGoal.Stat, pos) * 100f); string value2 = ((num >= 70) ? "#d4c48a" : "#8a7f6e"); if (stringBuilder.Length > 0) { stringBuilder.Append(" "); } stringBuilder.Append("<color=").Append(value2).Append('>') .Append(value) .Append(num) .Append("%</color>"); } } return stringBuilder.ToString(); } public unsafe static float EstimateProgressToward(PlayerStatType stat, Vector3 pos) { //IL_0020: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) string text = ((object)(*(PlayerStatType*)(&stat))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); float num; if (text.Contains("north")) { num = pos.z; } else if (text.Contains("south")) { num = 0f - pos.z; } else if (text.Contains("east")) { num = pos.x; } else { if (!text.Contains("west")) { return 0f; } num = 0f - pos.x; } return Mathf.Clamp01(num / 10350f); } public static string CardinalLabel(string statName) { if (string.IsNullOrEmpty(statName)) { return "?"; } string text = statName.ToLowerInvariant(); if (text.Contains("north")) { return "N"; } if (text.Contains("south")) { return "S"; } if (text.Contains("east")) { return "E"; } if (text.Contains("west")) { return "W"; } return statName.Substring(0, 1).ToUpperInvariant(); } } public static class PinnedAchievements { public const int MaxPins = 3; private static readonly List<string> _ids = new List<string>(3); private static bool _loaded; public static IReadOnlyList<string> Ids { get { EnsureLoaded(); return _ids; } } public static void EnsureLoaded() { if (!_loaded) { ReloadFromConfig(); } } public static void ReloadFromConfig() { _ids.Clear(); string[] array = (ModConfig.PinnedAchievementIds?.Value ?? "").Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string id = text.Trim(); if (!string.IsNullOrEmpty(id) && !_ids.Exists((string x) => string.Equals(x, id, StringComparison.OrdinalIgnoreCase))) { _ids.Add(id); if (_ids.Count >= 3) { break; } } } _loaded = true; } public static bool IsPinned(string id) { if (string.IsNullOrEmpty(id)) { return false; } EnsureLoaded(); return _ids.Exists((string x) => string.Equals(x, id, StringComparison.OrdinalIgnoreCase)); } public static bool CanPinMore() { EnsureLoaded(); return _ids.Count < 3; } public static bool Pin(string id) { if (string.IsNullOrEmpty(id)) { return false; } EnsureLoaded(); if (IsPinned(id)) { return true; } if (_ids.Count >= 3) { return false; } _ids.Add(id); Persist(); return true; } public static bool Unpin(string id) { if (string.IsNullOrEmpty(id)) { return false; } EnsureLoaded(); if (_ids.RemoveAll((string x) => string.Equals(x, id, StringComparison.OrdinalIgnoreCase)) <= 0) { return false; } Persist(); return true; } public static bool PruneInvalid() { EnsureLoaded(); if (!AchievementCatalog.IsReady) { return false; } int count = _ids.Count; _ids.RemoveAll(delegate(string id) { if (!AchievementCatalog.TryGetById(id, out var tracked) || (Object)(object)tracked?.Source == (Object)null) { return true; } if (tracked.Source.m_unlocked) { return true; } return ModConfig.IsAchievementDisabled(id) ? true : false; }); if (_ids.Count == count) { return false; } Persist(); return true; } public static IEnumerable<TrackedAchievement> GetPinnedTracked() { EnsureLoaded(); foreach (string id in _ids) { if (AchievementCatalog.TryGetById(id, out var tracked) && tracked != null) { yield return tracked; } } } private static void Persist() { if (ModConfig.PinnedAchievementIds != null) { ModConfig.PinnedAchievementIds.Value = string.Join(",", _ids); ModConfig.Save(); Plugin.LogDebug("Pins guardados: " + ModConfig.PinnedAchievementIds.Value); } } public static void OnConfigChanged() { _loaded = false; ReloadFromConfig(); } } public enum ProgressKind { Counter, Collection, Meta, Binary } public struct IncompleteGoal { public string Label; public float Current; public float Total; public bool RichLine; } public static class ProgressReader { public static PlayerStats GetAchievementStats() { Game instance = Game.instance; PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null); if (val?.m_playerStats == null || val.m_playerStats.Length < 2) { return null; } return val.m_playerStats[1]; } public static float GetStat(PlayerStatType stat) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) PlayerStats achievementStats = GetAchievementStats(); if (achievementStats == null) { return 0f; } return achievementStats[stat]; } public static float GetDictAmount(TrackedAchievement.DictChannel channel, string key) { PlayerStats achievementStats = GetAchievementStats(); if (achievementStats == null || string.IsNullOrEmpty(key)) { return 0f; } Dictionary<string, float> dictionary = null; switch (channel) { case TrackedAchievement.DictChannel.Enemy: return GetEnemyAmount(achievementStats, key); case TrackedAchievement.DictChannel.ItemPickup: dictionary = achievementStats.m_itemPickupStats; break; case TrackedAchievement.DictChannel.ItemCraft: dictionary = achievementStats.m_itemCraftStats; break; case TrackedAchievement.DictChannel.FoodEaten: dictionary = achievementStats.m_foodEatenStats; break; case TrackedAchievement.DictChannel.Pickable: dictionary = achievementStats.m_pickableStats; break; case TrackedAchievement.DictChannel.PiecePlaced: dictionary = achievementStats.m_piecesPlacedStats; break; } if (dictionary != null && dictionary.TryGetValue(key, out var value)) { return value; } return 0f; } private static float GetEnemyAmount(PlayerStats stats, string key) { if (stats.m_enemyStats == null) { return 0f; } float num = 0f; Dictionary<string, float>[] enemyStats = stats.m_enemyStats; foreach (Dictionary<string, float> dictionary in enemyStats) { if (dictionary != null && dictionary.TryGetValue(key, out var value)) { num += value; } } return num; } public static void GetProgress(TrackedAchievement tracked, out float current, out float total, out int completedParts, out int totalParts) { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) current = 0f; total = 0f; completedParts = 0; totalParts = 0; if (tracked == null) { return; } if (tracked.Kind == ProgressKind.Meta) { totalParts = tracked.MetaGoals.Count; foreach (Achievement metaGoal in tracked.MetaGoals) { if ((Object)(object)metaGoal != (Object)null && metaGoal.m_unlocked) { completedParts++; } } current = completedParts; total = totalParts; return; } if (tracked.Kind == ProgressKind.Counter && tracked.StatGoals.Count >= 1 && tracked.DictGoals.Count == 0) { float num = 0f; float num2 = 0f; foreach (TrackedAchievement.StatGoal statGoal in tracked.StatGoals) { float stat = GetStat(statGoal.Stat); num += Mathf.Min(stat, statGoal.Target); num2 += statGoal.Target; if (stat >= statGoal.Target) { completedParts++; } } current = num; total = num2; totalParts = tracked.StatGoals.Count; return; } totalParts = tracked.StatGoals.Count + tracked.DictGoals.Count; float num3 = 0f; float num4 = 0f; foreach (TrackedAchievement.StatGoal statGoal2 in tracked.StatGoals) { float stat2 = GetStat(statGoal2.Stat); num3 += Mathf.Min(stat2, statGoal2.Target); num4 += statGoal2.Target; if (stat2 >= statGoal2.Target) { completedParts++; } } foreach (TrackedAchievement.DictGoal dictGoal in tracked.DictGoals) { float dictAmount = GetDictAmount(dictGoal.Channel, dictGoal.Key); num3 += Mathf.Min(dictAmount, dictGoal.Target); num4 += dictGoal.Target; if (dictAmount >= dictGoal.Target) { completedParts++; } } if (tracked.Kind == ProgressKind.Collection) { current = completedParts; total = totalParts; } else { current = num3; total = ((num4 > 0f) ? num4 : ((float)totalParts)); } } public static bool IsDirectionalComplete(TrackedAchievement tracked, PlayerStatType stat) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) foreach (TrackedAchievement.StatGoal statGoal in tracked.StatGoals) { if (EqualityComparer<PlayerStatType>.Default.Equals(statGoal.Stat, stat)) { return GetStat(stat) >= statGoal.Target; } } return false; } public unsafe static void GetIncompleteGoals(TrackedAchievement tracked, Vector3 playerPos, List<IncompleteGoal> into, int maxRows = 8) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) into?.Clear(); if (tracked == null || into == null || maxRows <= 0) { return; } if (DirectionalProgress.IsDirectionalAchievement(tracked)) { string text = DirectionalProgress.FormatIncompleteDirections(tracked, playerPos); if (!string.IsNullOrEmpty(text)) { into.Add(new IncompleteGoal { Label = text, Current = 0f, Total = 0f, RichLine = true }); } if (tracked.Id == "ExploreNSEWNoMap") { bool flag = false; try { flag = (Object)(object)Achievements.m_instance != (Object)null && Achievements.IsCleanNoMap(); } catch { flag = false; } if (!flag) { into.Add(new IncompleteGoal { Label = "Requiere modificador: Sin mapa", Current = 0f, Total = 1f, RichLine = false }); } } return; } if (tracked.Kind == ProgressKind.Meta) { foreach (Achievement metaGoal in tracked.MetaGoals) { if (!((Object)(object)metaGoal == (Object)null) && !metaGoal.m_unlocked) { if (into.Count >= maxRows) { break; } into.Add(new IncompleteGoal { Label = AchievementCatalog.LocalizeName(metaGoal), Current = 0f, Total = 1f, RichLine = false }); } } return; } foreach (TrackedAchievement.StatGoal statGoal in tracked.StatGoals) { float stat = GetStat(statGoal.Stat); if (!(stat >= statGoal.Target)) { if (into.Count >= maxRows) { break; } IncompleteGoal item = default(IncompleteGoal); PlayerStatType stat2 = statGoal.Stat; item.Label = LocalizeStatKey(((object)(*(PlayerStatType*)(&stat2))/*cast due to .constrained prefix*/).ToString()); item.Current = stat; item.Total = statGoal.Target; item.RichLine = false; into.Add(item); } } foreach (TrackedAchievement.DictGoal dictGoal in tracked.DictGoals) { float dictAmount = GetDictAmount(dictGoal.Channel, dictGoal.Key); if (!(dictAmount >= dictGoal.Target)) { if (into.Count >= maxRows) { break; } into.Add(new IncompleteGoal { Label = LocalizeDictKey(dictGoal.Key), Current = dictAmount, Total = dictGoal.Target, RichLine = false }); } } } public static int CountIncompleteGoals(TrackedAchievement tracked) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (tracked == null) { return 0; } if (DirectionalProgress.IsDirectionalAchievement(tracked)) { int num = 0; { foreach (TrackedAchievement.StatGoal statGoal in tracked.StatGoals) { if (GetStat(statGoal.Stat) < statGoal.Target) { num++; } } return num; } } if (tracked.Kind == ProgressKind.Meta) { int num2 = 0; { foreach (Achievement metaGoal in tracked.MetaGoals) { if ((Object)(object)metaGoal != (Object)null && !metaGoal.m_unlocked) { num2++; } } return num2; } } int num3 = 0; foreach (TrackedAchievement.StatGoal statGoal2 in tracked.StatGoals) { if (GetStat(statGoal2.Stat) < statGoal2.Target) { num3++; } } foreach (TrackedAchievement.DictGoal dictGoal in tracked.DictGoals) { if (GetDictAmount(dictGoal.Channel, dictGoal.Key) < dictGoal.Target) { num3++; } } return num3; } private static string LocalizeStatKey(string statKey) { string text = "$stat_" + statKey; try { if (Localization.instance != null) { string text2 = Localization.instance.Localize(text); if (!string.IsNullOrEmpty(text2) && text2 != text) { return text2; } } } catch { } return statKey; } private static string LocalizeDictKey(string key) { if (string.IsNullOrEmpty(key)) { return "?"; } try { if (Localization.instance != null) { string text = Localization.instance.Localize(key); if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } return key; } public static string FormatAmount(float value) { if (Mathf.Abs(value - Mathf.Round(value)) < 0.001f) { return Mathf.RoundToInt(value).ToString(); } return value.ToString("0.#"); } } public sealed class ProgressWatcher : MonoBehaviour { private struct PendingToast { public TrackedAchievement Tracked; public float Current; public float Total; } private struct StatEvent { public PlayerStatType Stat; public float Before; public float After; } private struct DictEvent { public TrackedAchievement.DictChannel Channel; public string Key; public float Before; public float After; } private readonly Dictionary<string, PendingToast> _pending = new Dictionary<string, PendingToast>(); private readonly Dictionary<string, float> _coalesceDeadline = new Dictionary<string, float>(); private readonly Dictionary<string, float> _lastShownCurrent = new Dictionary<string, float>(); private readonly Queue<StatEvent> _statEvents = new Queue<StatEvent>(); private readonly Queue<DictEvent> _dictEvents = new Queue<DictEvent>(); private readonly object _eventLock = new object(); public static ProgressWatcher Instance { get; private set; } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { if (ModConfig.Enabled.Value) { if (!AchievementCatalog.IsReady && (Object)(object)Achievements.m_instance != (Object)null) { AchievementCatalog.Rebuild(); } DrainDeferredEvents(); if (ModConfig.EnableNotifications.Value) { FlushReadyPending(); } } } public void EnqueueStatChange(PlayerStatType stat, float before, float after) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (after <= before + 0.001f) { return; } lock (_eventLock) { _statEvents.Enqueue(new StatEvent { Stat = stat, Before = before, After = after }); } } public void EnqueueDictChange(TrackedAchievement.DictChannel channel, string key, float before, float after) { if (string.IsNullOrEmpty(key) || after <= before + 0.001f) { return; } lock (_eventLock) { _dictEvents.Enqueue(new DictEvent { Channel = channel, Key = key, Before = before, After = after }); } } private void DrainDeferredEvents() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) List<StatEvent> list = null; List<DictEvent> list2 = null; lock (_eventLock) { if (_statEvents.Count > 0) { list = new List<StatEvent>(_statEvents.Count); while (_statEvents.Count > 0) { list.Add(_statEvents.Dequeue()); } } if (_dictEvents.Count > 0) { list2 = new List<DictEvent>(_dictEvents.Count); while (_dictEvents.Count > 0) { list2.Add(_dictEvents.Dequeue()); } } } if (list != null) { foreach (StatEvent item in list) { ProcessStatChanged(item.Stat, item.Before, item.After); } } if (list2 == null) { return; } foreach (DictEvent item2 in list2) { ProcessDictChanged(item2.Channel, item2.Key, item2.Before, item2.After); } } private void ProcessStatChanged(PlayerStatType stat, float before, float after) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!ShouldProcess()) { return; } if (!AchievementCatalog.IsReady) { AchievementCatalog.Rebuild(); } if (after <= before + 0.001f || !AchievementCatalog.TryGetByStat(stat, out var list)) { return; } float num = after - before; foreach (TrackedAchievement item in list) { if (!ModConfig.HideCompletedRequirements.Value || !WasStatGoalAlreadyComplete(item, stat, before)) { Evaluate(item, Mathf.Max(0.01f, num)); } } } private void ProcessDictChanged(TrackedAchievement.DictChannel channel, string key, float before, float after) { if (!ShouldProcess() || string.IsNullOrEmpty(key)) { return; } if (!AchievementCatalog.IsReady) { AchievementCatalog.Rebuild(); } if (after <= before + 0.001f || !AchievementCatalog.TryGetByDict(channel, key, out var list)) { return; } float num = after - before; foreach (TrackedAchievement item in list) { if (!ModConfig.HideCompletedRequirements.Value || !WasDictGoalAlreadyComplete(item, channel, key, before)) { Evaluate(item, Mathf.Max(0.01f, num)); } } } private static bool WasDictGoalAlreadyComplete(TrackedAchievement tracked, TrackedAchievement.DictChannel channel, string key, float before) { if (tracked?.DictGoals == null) { return false; } foreach (TrackedAchievement.DictGoal dictGoal in tracked.DictGoals) { if (dictGoal.Channel == channel && string.Equals(dictGoal.Key, key, StringComparison.OrdinalIgnoreCase)) { return before >= dictGoal.Target; } } return false; } private static bool WasStatGoalAlreadyComplete(TrackedAchievement tracked, PlayerStatType stat, float before) { //IL_002c: 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 (tracked?.StatGoals == null) { return false; } foreach (TrackedAchievement.StatGoal statGoal in tracked.StatGoals) { if (EqualityComparer<PlayerStatType>.Default.Equals(statGoal.Stat, stat)) { return before >= statGoal.Target; } } return false; } public void SeedProgressBaselines() { if (!AchievementCatalog.IsReady) { return; } foreach (TrackedAchievement item in AchievementCatalog.All) { if (!((Object)(object)item?.Source == (Object)null)) { ProgressReader.GetProgress(item, out var current2, out var _, out var _, out var _); _lastShownCurrent[item.Id] = current2; } } Plugin.LogDebug($"Baselines de progreso sembradas ({_lastShownCurrent.Count})."); } public void OnAchievementUnlocked(Achievement ach) { if (!ShouldProcess() || (Object)(object)ach == (Object)null) { return; } foreach (TrackedAchievement item in AchievementCatalog.MetaAchievements()) { if ((Object)(object)item.Source != (Object)null && !item.Source.m_unlocked) { Evaluate(item, 1f); } } if (ModConfig.EnableBinaryUnlockToasts.Value) { TrackedAchievement trackedAchievement = FindById(ach.m_id); if (trackedAchievement != null && trackedAchievement.Kind == ProgressKind.Binary && !ModConfig.IsAchievementDisabled(trackedAchievement.Id)) { ToastNotifier.Show(trackedAchievement, 1f, 1f, 1f); } } } private TrackedAchievement FindById(string id) { foreach (TrackedAchievement item in AchievementCatalog.All) { if (item.Id == id) { return item; } } return null; } private bool ShouldProcess() { if (ModConfig.Enabled.Value && ModConfig.EnableNotifications.Value && (Object)(object)Game.instance != (Object)null) { return (Object)(object)Player.m_localPlayer != (Object)null; } return false; } private void Evaluate(TrackedAchievement tracked, float estimateDelta) { if ((Object)(object)tracked?.Source == (Object)null || tracked.Source.m_unlocked || ModConfig.IsAchievementDisabled(tracked.Id)) { return; } switch (tracked.Kind) { case ProgressKind.Counter: if (!ModConfig.EnableCounterToasts.Value) { return; } break; case ProgressKind.Collection: if (!ModConfig.EnableCollectionToasts.Value) { return; } break; case ProgressKind.Meta: if (!ModConfig.EnableMetaToasts.Value) { return; } break; case ProgressKind.Binary: return; } ProgressReader.GetProgress(tracked, out var current, out var total, out var _, out var _); if (total <= 0f) { return; } if (!_lastShownCurrent.TryGetValue(tracked.Id, out var value)) { _lastShownCurrent[tracked.Id] = current; } else { if (current <= value + 0.001f) { return; } if (UsesCoalesceDelay(tracked)) { if (!IsSailAchievement(tracked.Id) || !(current - value < 1f) || _pending.ContainsKey(tracked.Id)) { ScheduleFinalToast(tracked, current, total); } } else { float delta = Mathf.Max(estimateDelta, current - value); ShowNow(tracked, delta, current, total); } } } private static bool IsSailAchievement(string id) { if (!(id == "GrindSail")) { return id == "GrindSailHelm"; } return true; } private static bool UsesCoalesceDelay(TrackedAchievement tracked) { if (tracked == null) { return false; } if (!tracked.UseSpamCooldown && tracked.Kind != ProgressKind.Counter) { return IsSailAchievement(tracked.Id); } return true; } private float GetCooldownSeconds(TrackedAchievement tracked) { if (tracked != null && IsSailAchievement(tracked.Id)) { return Mathf.Max(1f, ModConfig.SailCooldownSeconds.Value); } return Mathf.Max(0.1f, ModConfig.SpamCooldownSeconds.Value); } private void ScheduleFinalToast(TrackedAchievement tracked, float current, float total) { float cooldownSeconds = GetCooldownSeconds(tracked); _pending[tracked.Id] = new PendingToast { Tracked = tracked, Current = current, Total = total }; _coalesceDeadline[tracked.Id] = Time.unscaledTime + cooldownSeconds; } private void FlushReadyPending() { if (_pending.Count == 0) { return; } float unscaledTime = Time.unscaledTime; List<string> list = new List<string>(); foreach (KeyValuePair<string, PendingToast> item in _pending) { if (_coalesceDeadline.TryGetValue(item.Key, out var value) && unscaledTime >= value) { list.Add(item.Key); } } foreach (string item2 in list) { PendingToast pendingToast = _pending[item2]; _pending.Remove(item2); _coalesceDeadline.Remove(item2); if (!((Object)(object)pendingToast.Tracked?.Source == (Object)null) && !pendingToast.Tracked.Source.m_unlocked) { _lastShownCurrent.TryGetValue(item2, out var value2); float num = Mathf.Max(0f, pendingToast.Current - value2); if (!(num < 0.001f)) { ShowNow(pendingToast.Tracked, num, pendingToast.Current, pendingToast.Total); } } } } private void ShowNow(TrackedAchievement tracked, float delta, float current, float total) { ToastNotifier.Show(tracked, delta, current, total); _lastShownCurrent[tracked.Id] = current; } public void ResetSession() { _pending.Clear(); _coalesceDeadline.Clear(); _lastShownCurrent.Clear(); lock (_eventLock) { _statEvents.Clear(); _dictEvents.Clear(); } } } public sealed class TrackedAchievement { public struct StatGoal { public PlayerStatType Stat; public float Target; } public struct DictGoal { public DictChannel Channel; public string Key; public float Target; } public enum DictChannel { Enemy, ItemPickup, ItemCraft, FoodEaten, Pickable, PiecePlaced } public Achievement Source; public string Id; public ProgressKind Kind; public bool UseSpamCooldown; public readonly List<StatGoal> StatGoals = new List<StatGoal>(); public readonly List<DictGoal> DictGoals = new List<DictGoal>(); public readonly List<Achievement> MetaGoals = new List<Achievement>(); } } namespace AchievementProgress.Config { public static class ModConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> EnableNotifications; public static ConfigEntry<bool> EnableDirectionalIndicator; public static ConfigEntry<string> PinnedAchievementIds; public static ConfigEntry<bool> RevealHiddenRequirements; public static ConfigEntry<bool> HideCompletedRequirements; public static ConfigEntry<string> IndicatorAnchor; public static ConfigEntry<float> IndicatorOffsetX; public static ConfigEntry<float> IndicatorOffsetY; public static ConfigEntry<float> IndicatorWidth; public static ConfigEntry<bool> IndicatorAutoHideOverlays; public static ConfigEntry<bool> IndicatorUseCustomPosition; public static ConfigEntry<float> IndicatorCustomNormX; public static ConfigEntry<float> IndicatorCustomNormY; private static ConfigFile _config; public static ConfigEntry<bool> EnableCounterToasts; public static ConfigEntry<bool> EnableCollectionToasts; public static ConfigEntry<bool> EnableMetaToasts; public static ConfigEntry<bool> EnableBinaryUnlockToasts; public static ConfigEntry<float> SpamCooldownSeconds; public static ConfigEntry<float> SailCooldownSeconds; public static ConfigEntry<bool> ShowPlusDelta; public static ConfigEntry<bool> ShowTotal; public static ConfigEntry<bool> RevealSecretsInToasts; public static ConfigEntry<bool> ReplaceUnlockLogBanner; public static ConfigEntry<string> ProgressBannerText; public static ConfigEntry<string> DisabledAchievementIds; public static ConfigEntry<bool> EnableDebugLogs; private static HashSet<string> _disabledIds; public static void Bind(ConfigFile config) { _config = config; Enabled = config.Bind<bool>("General", "Enabled", true, "Activa o desactiva el mod por completo."); EnableNotifications = config.Bind<bool>("General", "EnableNotifications", true, "Muestra notificaciones de progreso de logros."); EnableDirectionalIndicator = config.