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 AchievementTracker v1.1.0
AchievementTracker.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; 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("AchievementTracker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+93959c30b9df91cb0a16bb84ee7735b3aaebd06a")] [assembly: AssemblyProduct("AchievementTracker")] [assembly: AssemblyTitle("AchievementTracker")] [assembly: AssemblyVersion("1.1.0.0")] [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 AchievementTracker { internal static class AchievementPanel { private enum Filter { Available, Partial, Locked, Done, Tracked, All } [CompilerGenerated] private static class <>O { public static UnityAction <0>__Close; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__34_0; public static Action<Vector2> <>9__34_1; public static Func<Achievement, AchState> <>9__35_0; public static Func<AchState, bool> <>9__35_1; public static Func<AchState, bool> <>9__35_2; public static Func<AchState, bool> <>9__35_3; public static Func<AchState, bool> <>9__35_4; public static Func<AchState, float> <>9__35_7; public static Func<AchState, bool> <>9__35_8; public static Func<AchState, bool> <>9__39_0; public static Func<ReqState, int> <>9__42_0; public static Func<ReqState, float> <>9__42_1; internal void <Build>b__34_0() { if (!string.IsNullOrEmpty(s_selectedId)) { Tracked.Toggle(s_selectedId); Refresh(); } } internal void <Build>b__34_1(Vector2 size) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Plugin.WindowWidth.Value = Mathf.RoundToInt(size.x); Plugin.WindowHeight.Value = Mathf.RoundToInt(size.y); } internal AchState <Refresh>b__35_0(Achievement a) { return Analyzer.Evaluate(a, withAvailability: true); } internal bool <Refresh>b__35_1(AchState s) { return s.Status == AchStatus.Done; } internal bool <Refresh>b__35_2(AchState s) { return s.Status == AchStatus.Available; } internal bool <Refresh>b__35_3(AchState s) { return s.Status == AchStatus.Partial; } internal bool <Refresh>b__35_4(AchState s) { return s.Status == AchStatus.Locked; } internal float <Refresh>b__35_7(AchState s) { if (s.Status != AchStatus.Done) { return s.Fraction; } return -1f; } internal bool <Refresh>b__35_8(AchState s) { return s.Ach.m_id != s_selectedId; } internal bool <UpdateDetails>b__39_0(AchState s) { return s.Ach.m_id == s_selectedId; } internal int <BuildDetails>b__42_0(ReqState r) { if (!r.Met) { if (r.Avail != Avail.Available) { if (r.Avail != Avail.Unknown) { return 2; } return 1; } return 0; } return 3; } internal float <BuildDetails>b__42_1(ReqState r) { return Analyzer.ReqFraction(r.Req, r.Current); } } private const float MinW = 960f; private const float MinH = 520f; private const float Split = 0.56f; private static GameObject s_root; private static bool s_open; private static int s_closedFrame = -1; private static Filter s_filter = Filter.Available; private static string s_selectedId; private static Text s_summary; private static Button[] s_filterButtons; private static RectTransform s_listContent; private static ScrollRect s_listScroll; private static RectTransform s_detailsContent; private static ScrollRect s_detailsScroll; private static Button s_trackButton; private static List<AchState> s_states = new List<AchState>(); private static bool s_nativeScroll; private static string[] FilterNames { get { if (Loc.Ru) { return new string[6] { "Можно выполнить", "Частично", "Недоступно", "Выполнено", "Отслеживается", "Все" }; } return new string[6] { "Doable now", "Partly", "Locked", "Completed", "Tracked", "All" }; } } private static float F => Mathf.Max(0.5f, (float)Plugin.FontSize.Value / 15f); public static bool IsOpen { get { if (s_open) { return (Object)(object)s_root != (Object)null; } return false; } } public static bool JustClosed => Time.frameCount - s_closedFrame <= 1; private static int Fs(float basePx) { return Mathf.RoundToInt(basePx * F); } public static void Toggle() { if (IsOpen) { Close(); } else { Open(); } } public static void Open() { if (!Analyzer.Ready || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)GUIManager.CustomGUIFront == (Object)null) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, Loc.S("Достижения ещё не загрузились", "Achievements are not loaded yet"), 0, (Sprite)null, false); } return; } if ((Object)(object)s_root == (Object)null) { Build(); } s_root.SetActive(true); if (!s_open) { GUIManager.BlockInput(true); } s_open = true; Refresh(); } public static void Rebuild() { bool isOpen = IsOpen; if (isOpen) { Close(); } if ((Object)(object)s_root != (Object)null) { Object.Destroy((Object)(object)s_root); } s_root = null; if (isOpen) { Open(); } } public static void Close() { if ((Object)(object)s_root != (Object)null) { s_root.SetActive(false); } if (s_open) { GUIManager.BlockInput(false); } s_open = false; s_closedFrame = Time.frameCount; } private static float NativeScrollSensitivity() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return -1f; } ScrollRect val = null; if ((Object)(object)instance.m_achievementsListRoot != (Object)null) { val = ((Component)instance.m_achievementsListRoot).GetComponentInParent<ScrollRect>(true); } if ((Object)(object)val == (Object)null && (Object)(object)instance.m_recipeListRoot != (Object)null) { val = ((Component)instance.m_recipeListRoot).GetComponentInParent<ScrollRect>(true); } if (!((Object)(object)val != (Object)null)) { return -1f; } return val.scrollSensitivity; } public static void HandleScroll() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_0085: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00db: 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) if (!IsOpen || s_nativeScroll) { return; } float mouseScrollWheel = ZInput.GetMouseScrollWheel(); if (Mathf.Abs(mouseScrollWheel) < 0.0001f) { return; } Vector2 val = Vector2.op_Implicit(ZInput.pointerPosition); ScrollRect val2 = null; if (RectTransformUtility.RectangleContainsScreenPoint((RectTransform)((Component)s_listScroll).transform, val, (Camera)null)) { val2 = s_listScroll; } else if (RectTransformUtility.RectangleContainsScreenPoint((RectTransform)((Component)s_detailsScroll).transform, val, (Camera)null)) { val2 = s_detailsScroll; } if (!((Object)(object)val2 == (Object)null)) { RectTransform content = val2.content; Rect rect = content.rect; float height = ((Rect)(ref rect)).height; rect = val2.viewport.rect; float num = Mathf.Max(0f, height - ((Rect)(ref rect)).height); if (!(num <= 0f)) { float num2 = content.anchoredPosition.y - Mathf.Sign(mouseScrollWheel) * 80f; val2.StopMovement(); content.anchoredPosition = new Vector2(content.anchoredPosition.x, Mathf.Clamp(num2, 0f, num)); } } } public static void CheckAlive() { if (s_open && (Object)(object)s_root == (Object)null) { s_open = false; GUIManager.BlockInput(false); } } private static void Build() { //IL_0045: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Expected O, but got Unknown //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Expected O, but got Unknown //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0413: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_04a3: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Expected O, but got Unknown //IL_04c3: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04e1: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Expected O, but got Unknown //IL_052a: Unknown result type (might be due to invalid IL or missing references) //IL_052f: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Expected O, but got Unknown float num = Mathf.Max(960f, (float)Plugin.WindowWidth.Value); float num2 = Mathf.Max(520f, (float)Plugin.WindowHeight.Value); s_root = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, num, num2, true); ((Object)s_root).name = "AchievementTrackerPanel"; Transform transform = s_root.transform; TopStretch(((Graphic)UiKit.Label(transform, Names.L("$inventory_achievements"), 34, UiKit.Orange, (TextAnchor)4, title: true)).rectTransform, 0f, 14f, 0f, 44f); s_summary = UiKit.Label(transform, "", Fs(16f), UiKit.Beige, (TextAnchor)4); TopStretch(((Graphic)s_summary).rectTransform, 0f, 58f, 0f, 26f); s_filterButtons = (Button[])(object)new Button[FilterNames.Length]; float[] array = new float[6] { 200f, 130f, 145f, 140f, 185f, 100f }; float num3 = 8f; float num4 = (0f - (array.Sum() + num3 * (float)(FilterNames.Length - 1))) / 2f; Vector2 val2 = default(Vector2); for (int i = 0; i < FilterNames.Length; i++) { Filter f = (Filter)i; s_filterButtons[i] = UiKit.Button(transform, FilterNames[i], 0f, 0f, array[i], 34f, (UnityAction)delegate { s_filter = f; s_selectedId = null; Refresh(); s_listScroll.verticalNormalizedPosition = 1f; }); RectTransform val = (RectTransform)((Component)s_filterButtons[i]).transform; ((Vector2)(ref val2))..ctor(0.5f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(num4, -92f); num4 += array[i] + num3; } s_listScroll = UiKit.ScrollList(UiKit.Region(UiKit.Rect("List", transform), new Vector2(0f, 0f), new Vector2(0.56f, 1f), new Vector2(26f, 68f), new Vector2(-6f, -138f)), out s_listContent); string text = Loc.S("Отслеживать", "Track"); object obj = <>c.<>9__34_0; if (obj == null) { UnityAction val3 = delegate { if (!string.IsNullOrEmpty(s_selectedId)) { Tracked.Toggle(s_selectedId); Refresh(); } }; <>c.<>9__34_0 = val3; obj = (object)val3; } s_trackButton = UiKit.Button(transform, text, 0f, 0f, 100f, 36f, (UnityAction)obj); UiKit.Region((RectTransform)((Component)s_trackButton).transform, new Vector2(0.56f, 1f), new Vector2(1f, 1f), new Vector2(8f, -174f), new Vector2(-26f, -138f)); s_detailsScroll = UiKit.ScrollList(UiKit.Region(UiKit.Rect("Details", transform), new Vector2(0.56f, 0f), new Vector2(1f, 1f), new Vector2(8f, 68f), new Vector2(-26f, -182f)), out s_detailsContent); ((LayoutGroup)((Component)s_detailsContent).GetComponent<VerticalLayoutGroup>()).padding = new RectOffset(8, 8, 8, 8); float num5 = NativeScrollSensitivity(); s_nativeScroll = num5 > 0f; if (s_nativeScroll) { s_listScroll.scrollSensitivity = num5; s_detailsScroll.scrollSensitivity = num5; } string text2 = ((object)Plugin.PanelKey.Value/*cast due to .constrained prefix*/).ToString(); UiKit.Pin(((Graphic)UiKit.Label(transform, Loc.S(text2 + " — открыть/закрыть · клик по достижению — подробности · уголок справа внизу — размер окна", text2 + " — open/close · click an achievement for details · bottom-right corner — resize"), 13, UiKit.Grey, (TextAnchor)3)).rectTransform, new Vector2(0f, 0f), new Vector2(30f, 22f), new Vector2(640f, 30f)); string text3 = Loc.S("Закрыть", "Close"); object obj2 = <>O.<0>__Close; if (obj2 == null) { UnityAction val4 = Close; <>O.<0>__Close = val4; obj2 = (object)val4; } UiKit.Pin((RectTransform)((Component)UiKit.Button(transform, text3, 0f, 0f, 160f, 36f, (UnityAction)obj2)).transform, new Vector2(1f, 0f), new Vector2(-34f, 18f), new Vector2(160f, 36f)); RectTransform obj3 = UiKit.Pin(UiKit.Rect("ResizeGrip", transform), new Vector2(1f, 0f), new Vector2(-6f, 6f), new Vector2(24f, 24f)); ((Component)obj3).gameObject.AddComponent<Image>().sprite = UiKit.GripSprite(); ResizeHandle resizeHandle = ((Component)obj3).gameObject.AddComponent<ResizeHandle>(); resizeHandle.Target = (RectTransform)s_root.transform; resizeHandle.Min = new Vector2(960f, 520f); resizeHandle.OnResized = delegate(Vector2 size) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Plugin.WindowWidth.Value = Mathf.RoundToInt(size.x); Plugin.WindowHeight.Value = Mathf.RoundToInt(size.y); }; } public static void Refresh() { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen || !Analyzer.Ready) { return; } s_states = (from a in Analyzer.All() select Analyzer.Evaluate(a, withAvailability: true)).ToList(); List<string> tracked = Tracked.Ids(); int count = s_states.Count; int num = s_states.Count((AchState s) => s.Status == AchStatus.Done); int num2 = s_states.Count((AchState s) => s.Status == AchStatus.Available); int num3 = s_states.Count((AchState s) => s.Status == AchStatus.Partial); int num4 = s_states.Count((AchState s) => s.Status == AchStatus.Locked); s_summary.text = string.Format("{0} <color=#{1}>{2}</color> {3} {4} · ", Loc.S("Выполнено", "Completed"), UiKit.Hex(UiKit.Green), num, Loc.S("из", "of"), count) + string.Format("{0} <color=#{1}>{2}</color> · ", Loc.S("можно выполнить", "doable now"), UiKit.Hex(UiKit.Yellow), num2) + string.Format("{0} <color=#{1}>{2}</color> · ", Loc.S("частично", "partly"), UiKit.Hex(UiKit.Orange), num3) + string.Format("{0} <color=#{1}>{2}</color>", Loc.S("недоступно", "locked"), UiKit.Hex(UiKit.Grey), num4); int[] array = new int[6] { num2, num3, num4, num, tracked.Count, count }; for (int num5 = 0; num5 < s_filterButtons.Length; num5++) { bool flag = s_filter == (Filter)num5; UiKit.SetButtonText(s_filterButtons[num5], $"{FilterNames[num5]} ({array[num5]})", flag ? UiKit.Yellow : UiKit.Beige); } List<AchState> list = (from s in s_states where Matches(s, tracked) orderby tracked.Contains(s.Ach.m_id) descending, (s.Status != AchStatus.Done) ? s.Fraction : (-1f) descending select s).ToList(); foreach (Transform item in (Transform)s_listContent) { Object.Destroy((Object)(object)((Component)item).gameObject); } if (list.Count == 0) { ((Component)UiKit.Label((Transform)(object)s_listContent, Loc.S("Здесь пусто", "Nothing here"), Fs(16f), UiKit.Grey, (TextAnchor)4)).gameObject.AddComponent<LayoutElement>().preferredHeight = 60f; } if (s_selectedId == null || list.All((AchState s) => s.Ach.m_id != s_selectedId)) { s_selectedId = list.FirstOrDefault()?.Ach.m_id; } foreach (AchState item2 in list) { BuildRow(item2, tracked.Contains(item2.Ach.m_id)); } UpdateDetails(); } private static bool Matches(AchState s, List<string> tracked) { return s_filter switch { Filter.Available => s.Status == AchStatus.Available, Filter.Partial => s.Status == AchStatus.Partial, Filter.Locked => s.Status == AchStatus.Locked, Filter.Done => s.Status == AchStatus.Done, Filter.Tracked => tracked.Contains(s.Ach.m_id), _ => true, }; } private static void BuildRow(AchState st, bool tracked) { //IL_0024: 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) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_01e9: 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_02a1: 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_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0369: 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_0390: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) Achievement ach = st.Ach; bool flag = ach.m_id == s_selectedId; Color val = UiKit.StatusColor(st.Status); float f = F; float num = 48f * f; float num2 = 16f + num; float num3 = 6f * f; float num4 = 24f * f; float num5 = num3 + num4 - 1f; float num6 = 20f * f; float num7 = Mathf.Ceil(num5 + num6 + 15f); RectTransform val2 = UiKit.Rect("Row", (Transform)(object)s_listContent); LayoutElement obj = ((Component)val2).gameObject.AddComponent<LayoutElement>(); float minHeight = (obj.preferredHeight = num7); obj.minHeight = minHeight; Image targetGraphic = UiKit.Bg(((Component)val2).gameObject, flag ? new Color(0.5f, 0.33f, 0.1f, 0.8f) : new Color(0f, 0f, 0f, 0.35f)); Button obj2 = ((Component)val2).gameObject.AddComponent<Button>(); ((Selectable)obj2).targetGraphic = (Graphic)(object)targetGraphic; string id = ach.m_id; ((UnityEvent)obj2.onClick).AddListener((UnityAction)delegate { s_selectedId = id; Refresh(); }); Sprite val3 = ((st.Hidden && (Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_secretAchievementIcon : (ach.m_unlocked ? ach.m_icon : (((Object)(object)ach.m_iconLocked != (Object)null) ? ach.m_iconLocked : ach.m_icon))); if ((Object)(object)val3 != (Object)null) { Image obj3 = ((Component)UiKit.TopLeft(UiKit.Rect("Icon", (Transform)(object)val2), 7f, (num7 - num) / 2f - 2f, num, num)).gameObject.AddComponent<Image>(); obj3.sprite = val3; obj3.preserveAspect = true; ((Graphic)obj3).raycastTarget = false; } string text = (tracked ? ("<color=#" + UiKit.Hex(UiKit.Yellow) + ">[" + Loc.S("отслеживается", "tracked") + "]</color> ") : ""); Text obj4 = UiKit.Label((Transform)(object)val2, text + Names.Achievement(ach), Fs(17f), val, (TextAnchor)0); TopStretch(((Graphic)obj4).rectTransform, num2, num3, 110f * f, num4); obj4.horizontalOverflow = (HorizontalWrapMode)1; string s = UiKit.StatusText(st.Status) + (string.IsNullOrEmpty(st.Summary) ? "" : (" — " + st.Summary)); Text obj5 = UiKit.Label((Transform)(object)val2, s, Fs(13f), UiKit.Grey, (TextAnchor)0); TopStretch(((Graphic)obj5).rectTransform, num2, num5, 12f, num6); obj5.horizontalOverflow = (HorizontalWrapMode)1; obj5.verticalOverflow = (VerticalWrapMode)0; Text obj6 = UiKit.Label((Transform)(object)val2, (st.Status == AchStatus.Done) ? Loc.S("готово", "done") : st.ProgressText, Fs(16f), val, (TextAnchor)2); obj6.horizontalOverflow = (HorizontalWrapMode)1; UiKit.Pin(((Graphic)obj6).rectTransform, new Vector2(1f, 1f), new Vector2(-10f, 0f - num3), new Vector2(110f * f, num4)); RectTransform fill; RectTransform obj7 = UiKit.ProgressBar((Transform)(object)val2, out fill); obj7.anchorMin = new Vector2(0f, 0f); obj7.anchorMax = new Vector2(1f, 0f); obj7.pivot = new Vector2(0.5f, 0f); obj7.offsetMin = new Vector2(num2, 6f); obj7.offsetMax = new Vector2(-10f, 11f); UiKit.SetFill(fill, st.Fraction, val); } private static void TopStretch(RectTransform rt, float left, float top, float right, float height) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = new Vector2(0f, 1f); rt.anchorMax = new Vector2(1f, 1f); rt.pivot = new Vector2(0f, 1f); rt.offsetMin = new Vector2(left, 0f - top - height); rt.offsetMax = new Vector2(0f - right, 0f - top); } private static void UpdateDetails() { AchState achState = s_states.FirstOrDefault((AchState s) => s.Ach.m_id == s_selectedId); if (achState == null) { SetDetails(new List<string> { Loc.S("Выбери достижение слева.", "Select an achievement on the left.") }); ((Selectable)s_trackButton).interactable = false; UiKit.SetButtonText(s_trackButton, Loc.S("Отслеживать", "Track")); } else { bool flag = Tracked.Contains(achState.Ach.m_id); ((Selectable)s_trackButton).interactable = flag || achState.Status != AchStatus.Done; UiKit.SetButtonText(s_trackButton, flag ? Loc.S("Не отслеживать", "Untrack") : Loc.S("Отслеживать (показывать на экране)", "Track (show on screen)")); SetDetails(BuildDetails(achState)); s_detailsScroll.verticalNormalizedPosition = 1f; } } private static string C(Color c, string s) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return "<color=#" + UiKit.Hex(c) + ">" + s + "</color>"; } private static void SetDetails(List<string> blocks) { //IL_0013: 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) foreach (Transform item in (Transform)s_detailsContent) { Object.Destroy((Object)(object)((Component)item).gameObject); } foreach (string block in blocks) { UiKit.Label((Transform)(object)s_detailsContent, block, Fs(15f), UiKit.Beige, (TextAnchor)0); } } private static List<string> BuildDetails(AchState st) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Invalid comparison between Unknown and I4 //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) Achievement ach = st.Ach; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"<size={Fs(22f)}>{C(UiKit.Orange, Names.Achievement(ach))}</size>"); string text = Names.Description(ach); if (!string.IsNullOrEmpty(text)) { stringBuilder.AppendLine("<i>" + text + "</i>"); } string value = (st.Hidden ? "" : Guide.Get(st)); if (!string.IsNullOrEmpty(value)) { stringBuilder.AppendLine(); stringBuilder.AppendLine(C(UiKit.Orange, Loc.S("<b>Как выполнить</b>", "<b>How to get it</b>"))); stringBuilder.AppendLine(value); } stringBuilder.AppendLine(); stringBuilder.AppendLine(Loc.S("Статус: ", "Status: ") + C(UiKit.StatusColor(st.Status), UiKit.StatusText(st.Status))); if (st.Status != AchStatus.Done) { stringBuilder.AppendLine(Loc.S("Прогресс: ", "Progress: ") + st.ProgressText); } if (!string.IsNullOrEmpty(st.DifficultyLock) && st.Status != AchStatus.Done) { stringBuilder.AppendLine(C(UiKit.Red, st.DifficultyLock)); } if ((int)ach.m_difficultyRequirement > 1) { stringBuilder.AppendLine(Loc.S("Сложность: ", "Difficulty: ") + Names.Difficulty(ach.m_difficultyRequirement)); } if (st.Hidden) { stringBuilder.AppendLine(); stringBuilder.AppendLine(C(UiKit.Grey, Loc.S("Секретное достижение. Чтобы видеть условия, включи RevealSecrets в конфиге мода.", "Secret achievement. Enable RevealSecrets in the mod config to see its requirements."))); return new List<string> { stringBuilder.ToString().TrimEnd(Array.Empty<char>()) }; } if (st.Reqs.Count == 0) { return new List<string> { stringBuilder.ToString().TrimEnd(Array.Empty<char>()) }; } stringBuilder.AppendLine(); stringBuilder.Append(C(UiKit.Beige, Loc.S("<b>Условия:</b>", "<b>Requirements:</b>"))); List<string> list = new List<string> { stringBuilder.ToString() }; foreach (ReqState item in from r in st.Reqs orderby (!r.Met) ? ((r.Avail != Avail.Available) ? ((r.Avail == Avail.Unknown) ? 1 : 2) : 0) : 3, Analyzer.ReqFraction(r.Req, r.Current) descending select r) { list.Add(ReqLine(item)); } return list; } private static string ReqLine(ReqState r) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Invalid comparison between Unknown and I4 //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Invalid comparison between Unknown and I4 //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) string text = ""; if (r.Req.Kind != ReqKind.OtherAchievement) { if ((int)r.Req.Op == 0) { if (r.Req.Target > 1f) { text = " " + Analyzer.Num(Math.Min(r.Current, r.Req.Target)) + " / " + Analyzer.Num(r.Req.Target); } } else { string text2 = (((int)r.Req.Op == 1) ? "<=" : (((int)r.Req.Op == 2) ? "=" : "!=")); text = Loc.S(" (сейчас " + (r.HasValue ? Analyzer.Num(r.Current) : "—") + ", нужно " + text2 + " " + Analyzer.Num(r.Req.Target) + ")", " (now " + (r.HasValue ? Analyzer.Num(r.Current) : "—") + ", need " + text2 + " " + Analyzer.Num(r.Req.Target) + ")"); } } if (r.Met) { return C(UiKit.Green, "+ " + r.Name + text); } string text3 = (string.IsNullOrEmpty(r.Reason) ? "" : ("\n " + C(UiKit.Grey, $"<size={Fs(13f)}>{r.Reason}</size>"))); return r.Avail switch { Avail.Available => C(UiKit.Yellow, "• " + r.Name + text) + text3, Avail.Unknown => C(UiKit.Orange, "? " + r.Name + text) + text3, _ => C(UiKit.Grey, "× " + r.Name + text) + text3, }; } } public enum ReqKind { Stat, Enemy, ItemPickup, ItemCraft, Pickable, FoodEaten, PiecePlaced, KnownWorld, KnownWorldKey, KnownCommand, OtherAchievement } public enum Avail { Met, Available, Unknown, Locked } public enum AchStatus { Done, Available, Partial, Locked } public class Requirement { public ReqKind Kind; public string Key; public PlayerStatType Stat; public KillModifiers Modifier; public RequirementOperator Op; public float Target; public Achievement Other; } public class ReqState { public Requirement Req; public bool HasValue; public float Current; public bool Met; public Avail Avail; public string Reason = ""; public string Name = ""; } public class AchState { public Achievement Ach; public List<ReqState> Reqs = new List<ReqState>(); public AchStatus Status; public bool Hidden; public int MetCount; public float Fraction; public string ProgressText = ""; public string Summary = ""; public string DifficultyLock; } public static class Analyzer { private static readonly CultureInfo Ru = CultureInfo.GetCultureInfo("ru-RU"); public const float LenientCap = 1.15f; public static bool Ready { get { if ((Object)(object)Achievements.m_instance != (Object)null && (Object)(object)Game.instance != (Object)null) { return Game.instance.GetPlayerProfile() != null; } return false; } } public static IEnumerable<Achievement> All() { if ((Object)(object)Achievements.m_instance == (Object)null) { yield break; } foreach (AchievementList achievementList in Achievements.m_instance.m_achievementLists) { if ((Object)(object)achievementList == (Object)null) { continue; } foreach (Achievement achievement in achievementList.m_achievements) { if ((Object)(object)achievement != (Object)null) { yield return achievement; } } } } public static Achievement Find(string id) { return All().FirstOrDefault((Func<Achievement, bool>)((Achievement a) => a.m_id == id)); } public static bool IsHidden(Achievement a) { if (a.m_isSecret && !a.m_unlocked) { return !Plugin.RevealSecrets.Value; } return false; } public unsafe static List<Requirement> GetRequirements(Achievement a) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) List<Requirement> list = new List<Requirement>(); foreach (PlayerStatRequirement item in a.m_statTrigger) { Requirement obj = new Requirement { Kind = ReqKind.Stat }; PlayerStatType stat = item.m_stat; obj.Key = ((object)(*(PlayerStatType*)(&stat))/*cast due to .constrained prefix*/).ToString(); obj.Stat = item.m_stat; obj.Target = item.m_amountAboveEquals; list.Add(obj); } foreach (EnemyStatRequirement enemyStatsTrigger in a.m_enemyStatsTriggers) { list.Add(new Requirement { Kind = ReqKind.Enemy, Key = enemyStatsTrigger.m_stat, Modifier = enemyStatsTrigger.m_modifier, Op = enemyStatsTrigger.m_operator, Target = enemyStatsTrigger.m_amount }); } AddDict(list, a.m_itemPickupTriggers, ReqKind.ItemPickup); AddDict(list, a.m_itemCraftTriggers, ReqKind.ItemCraft); AddDict(list, a.m_pickableTriggers, ReqKind.Pickable); AddDict(list, a.m_foodEatenTriggers, ReqKind.FoodEaten); AddDict(list, a.m_piecePlacedTriggers, ReqKind.PiecePlaced); AddDict(list, a.m_knownWorldTriggers, ReqKind.KnownWorld); AddDict(list, a.m_knownWorldKeysTriggers, ReqKind.KnownWorldKey); AddDict(list, a.m_knownCommandsTriggers, ReqKind.KnownCommand); foreach (Achievement otherAchievementTrigger in a.m_otherAchievementTriggers) { if (!((Object)(object)otherAchievementTrigger == (Object)null)) { list.Add(new Requirement { Kind = ReqKind.OtherAchievement, Key = otherAchievementTrigger.m_id, Other = otherAchievementTrigger, Target = 1f }); } } return list; } public static int RequirementCount(Achievement a) { return a.m_statTrigger.Count + a.m_enemyStatsTriggers.Count + a.m_itemPickupTriggers.Count + a.m_itemCraftTriggers.Count + a.m_pickableTriggers.Count + a.m_foodEatenTriggers.Count + a.m_piecePlacedTriggers.Count + a.m_knownWorldTriggers.Count + a.m_knownWorldKeysTriggers.Count + a.m_knownCommandsTriggers.Count + a.m_otherAchievementTriggers.Count; } private static void AddDict(List<Requirement> list, List<DictStatRequirement> src, ReqKind kind) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) foreach (DictStatRequirement item in src) { list.Add(new Requirement { Kind = kind, Key = item.m_stat, Op = item.m_operator, Target = item.m_amount }); } } public static PlayerStats StatsFor(Achievement a) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected I4, but got Unknown PlayerStats[] playerStats = Game.instance.GetPlayerProfile().m_playerStats; int num = (int)a.m_difficultyRequirement; if (num < 0 || num >= playerStats.Length) { return playerStats[1]; } return playerStats[num]; } public static bool TryGetValue(Requirement r, PlayerStats s, out float value) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected I4, but got Unknown value = 0f; switch (r.Kind) { case ReqKind.Stat: return s.m_stats.TryGetValue(r.Stat, out value); case ReqKind.Enemy: { int num = (int)r.Modifier; if (num >= 0 && num < s.m_enemyStats.Length) { return s.m_enemyStats[num].TryGetValue(r.Key, out value); } return false; } case ReqKind.ItemPickup: return s.m_itemPickupStats.TryGetValue(r.Key, out value); case ReqKind.ItemCraft: return s.m_itemCraftStats.TryGetValue(r.Key, out value); case ReqKind.Pickable: return s.m_pickableStats.TryGetValue(r.Key, out value); case ReqKind.FoodEaten: return s.m_foodEatenStats.TryGetValue(r.Key, out value); case ReqKind.PiecePlaced: return s.m_piecesPlacedStats.TryGetValue(r.Key, out value); case ReqKind.KnownWorld: return s.m_knownWorlds.TryGetValue(r.Key, out value); case ReqKind.KnownWorldKey: return s.m_knownWorldKeys.TryGetValue(r.Key, out value); case ReqKind.KnownCommand: return s.m_knownCommands.TryGetValue(r.Key, out value); case ReqKind.OtherAchievement: value = (((Object)(object)r.Other != (Object)null && r.Other.m_unlocked) ? 1f : 0f); return true; default: return false; } } public static bool IsMet(Requirement r, bool has, float v) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown if (!has) { return false; } RequirementOperator op = r.Op; return (op - 1) switch { 0 => v <= r.Target, 1 => v == r.Target, 2 => v != r.Target, _ => v >= r.Target, }; } public static string DifficultyLock(Achievement a, List<Requirement> reqs) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000d: 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_004c: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) //IL_005a: Unknown result type (might be due to invalid IL or missing references) DifficultyRequirement difficultyRequirement = a.m_difficultyRequirement; if ((int)difficultyRequirement <= 1) { return null; } DifficultyRequirement currentAchievementDifficulty = Achievements.GetCurrentAchievementDifficulty(); bool num = ((reqs.Count > 0 && reqs.All((Requirement r) => r.Kind == ReqKind.Enemy)) ? (currentAchievementDifficulty >= difficultyRequirement) : (currentAchievementDifficulty == difficultyRequirement)); string text = Names.Difficulty(difficultyRequirement); string text2 = Names.Difficulty(currentAchievementDifficulty); if (!num) { return Loc.S("нужна сложность мира «" + text + "» (сейчас «" + text2 + "»)", "requires world difficulty \"" + text + "\" (now \"" + text2 + "\")"); } return null; } public static AchState Evaluate(Achievement a, bool withAvailability) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) AchState achState = new AchState { Ach = a, Hidden = IsHidden(a) }; List<Requirement> requirements = GetRequirements(a); PlayerStats s = StatsFor(a); float num = 0f; foreach (Requirement item in requirements) { ReqState reqState = new ReqState { Req = item }; reqState.HasValue = TryGetValue(item, s, out reqState.Current); reqState.Met = IsMet(item, reqState.HasValue, reqState.Current); if (reqState.Met) { achState.MetCount++; } num += (reqState.Met ? 1f : ReqFraction(item, reqState.Current)); if (withAvailability) { reqState.Name = Names.Requirement(item); if (reqState.Met) { reqState.Avail = Avail.Met; } else { Progression.Check(item, out reqState.Avail, out reqState.Reason); } } achState.Reqs.Add(reqState); } int count = achState.Reqs.Count; if (count == 1 && requirements[0].Kind != ReqKind.OtherAchievement && (int)requirements[0].Op == 0) { ReqState reqState2 = achState.Reqs[0]; achState.ProgressText = Num(Math.Min(reqState2.Current, reqState2.Req.Target)) + " / " + Num(reqState2.Req.Target); } else { achState.ProgressText = achState.MetCount + " / " + count; } achState.Fraction = ((count != 0) ? (num / (float)count) : (a.m_unlocked ? 1f : 0f)); if (a.m_lenientBuildAchievement) { float num2 = 0f; float num3 = 0f; foreach (ReqState req in achState.Reqs) { if (req.Req.Kind == ReqKind.Stat) { num2 += req.Req.Target; num3 += Math.Min(req.Current, req.Req.Target * 1.15f); } } if (num2 > 0f) { achState.ProgressText = Num(Math.Min(num3, num2)) + " / " + Num(num2); achState.Fraction = Math.Min(1f, num3 / num2); } } if (a.m_unlocked) { achState.Status = AchStatus.Done; achState.Fraction = 1f; return achState; } achState.DifficultyLock = DifficultyLock(a, requirements); if (!withAvailability) { return achState; } if (achState.DifficultyLock != null) { achState.Status = AchStatus.Locked; achState.Summary = achState.DifficultyLock; return achState; } List<ReqState> list = achState.Reqs.Where((ReqState r) => !r.Met).ToList(); bool num4 = list.Any((ReqState r) => r.Avail == Avail.Locked); bool flag = list.Any((ReqState r) => r.Avail == Avail.Available || r.Avail == Avail.Unknown); if (!num4) { achState.Status = AchStatus.Available; } else if (flag) { achState.Status = AchStatus.Partial; } else { achState.Status = AchStatus.Locked; } if (achState.Status == AchStatus.Available) { ReqState reqState3 = list.FirstOrDefault((ReqState r) => r.Avail == Avail.Unknown); achState.Summary = ((reqState3 != null && list.All((ReqState r) => r.Avail == Avail.Unknown)) ? reqState3.Reason : ""); } else { IEnumerable<string> values = (from r in list where r.Avail == Avail.Locked && !string.IsNullOrEmpty(r.Reason) select r.Reason).Distinct().Take(2); achState.Summary = string.Join("; ", values); if (achState.Status == AchStatus.Partial) { int num5 = list.Count((ReqState r) => r.Avail != Avail.Locked); achState.Summary = Loc.S($"сейчас можно закрыть {num5} из {list.Count} оставшихся. ", $"{num5} of {list.Count} remaining doable now. ") + achState.Summary; } } return achState; } public static float ReqFraction(Requirement r, float cur) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)r.Op != 0 || r.Target <= 0f) { return 0f; } return Math.Max(0f, Math.Min(1f, cur / r.Target)); } public static string Num(float v) { if (Math.Abs((double)v - Math.Round(v)) < 0.009999999776482582 || Math.Abs(v) >= 100f) { return Math.Round(v).ToString("#,0", Ru).Replace('\u00a0', ' '); } return v.ToString("0.#", Ru); } } internal sealed class ConfigurationManagerAttributes { public string DispName; public string Description; public string Category; public int? Order; public bool? Browsable; } internal static class ConfigText { private static readonly Dictionary<string, string[]> Texts = new Dictionary<string, string[]> { ["Language"] = new string[4] { "Язык", "Language", "Язык интерфейса мода. Game — как в игре.", "Mod interface language. Game = same as the game." }, ["AchievementNames"] = new string[4] { "Названия достижений", "Achievement names", "На каком языке показывать названия достижений: Game — как в игре, English или Russian — на выбранном языке, Both — русское и английское сразу.", "Language of achievement names: Game — as in the game, English or Russian — in that language, Both — Russian and English at once." }, ["PanelKey"] = new string[4] { "Клавиша окна", "Window key", "Клавиша, которая открывает и закрывает окно достижений.", "Key that opens and closes the achievements window." }, ["FontSize"] = new string[4] { "Размер шрифта окна", "Window font size", "Размер текста в окне достижений; строки списка и иконки растут вместе с ним.", "Text size in the achievements window; list rows and icons grow with it." }, ["WindowWidth"] = new string[4] { "Ширина окна", "Window width", "Ширина окна достижений — меняется уголком в правом нижнем углу окна.", "Achievements window width — change it with the bottom-right corner of the window." }, ["WindowHeight"] = new string[4] { "Высота окна", "Window height", "Высота окна достижений — меняется уголком в правом нижнем углу окна.", "Achievements window height — change it with the bottom-right corner of the window." }, ["RevealSecrets"] = new string[4] { "Показывать секретные", "Reveal secrets", "Показывать названия и условия секретных достижений до их получения.", "Show names and requirements of secret achievements before unlocking them." }, ["MarkUncrafted"] = new string[4] { "Звёздочки у несделанного", "Mark unmade items", "Помечать звёздочкой то, что ещё нужно для достижений: в меню молота — непостроенное, на станках (верстак, кузница, котёл и т.д.) — нескрафченное, в инвентаре и сундуках — трофеи, которые ещё не подбирал, еду, которую ещё не ел, рыбу, которую ещё не ловил, сырьё для блюд с вертела и решётки, которые ещё не готовил, и семена для посадок, которые ещё не делал. Alt + наведение на предмет со звёздочкой — для каких достижений он нужен.", "Mark with a star what is still needed for achievements: unbuilt pieces in the hammer menu, uncrafted items at crafting stations (workbench, forge, cauldron etc.), and in inventory and chests — trophies not yet picked up, food not yet eaten, fish not yet caught, raw ingredients for spit/grill dishes not yet cooked and seeds for plantings not yet made. Hold Alt and hover a starred item to see which achievements need it." }, ["ToastsForAll"] = new string[4] { "Для всех достижений", "For all achievements", "Всплывающий счётчик для всех достижений. Если выключено — только для отслеживаемых.", "Progress popups for all achievements. When off — only for tracked ones." }, ["SkipTracked"] = new string[4] { "Не показывать для отслеживаемых", "Skip tracked", "Не показывать всплывающий счётчик для отслеживаемых достижений — их прогресс и так виден в панели на экране.", "Don't show progress popups for tracked achievements — their progress is already visible in the on-screen panel." }, ["ToastDuration"] = new string[4] { "Длительность (сек)", "Duration (sec)", "Сколько секунд висит всплывающий счётчик.", "How many seconds a progress popup stays on screen." }, ["ToastY"] = new string[4] { "Позиция по вертикали", "Vertical position", "Отступ всплывающих счётчиков от верхнего края экрана.", "Offset of progress popups from the top of the screen." }, ["ToastOpacity"] = new string[4] { "Непрозрачность фона (%)", "Background opacity (%)", "Насколько плотный фон у всплывающих счётчиков: 0 — полностью прозрачный, 100 — сплошной.", "How solid the popup background is: 0 — fully transparent, 100 — solid." }, ["Enabled"] = new string[4] { "Показывать на экране", "Show on screen", "Показывать отслеживаемые достижения и навыки на экране. Навыки выбираются галочкой слева в окне навыков или Alt + ЛКМ по строке навыка.", "Show tracked achievements and skills on screen. Select skills with the checkbox on the left of the skills window or Alt + left-click a skill row." }, ["PositionX"] = new string[4] { "Позиция по горизонтали", "Horizontal position", "Отступ списка отслеживаемых достижений и навыков от правого края экрана.", "Offset of the tracked list from the right edge of the screen." }, ["PositionY"] = new string[4] { "Позиция по вертикали", "Vertical position", "Отступ списка отслеживаемых достижений и навыков от верхнего края экрана.", "Offset of the tracked list from the top of the screen." }, ["ListLength"] = new string[4] { "Длина списка", "List length", "Сколько условий, которые можно выполнить сейчас, показывать под каждым отслеживаемым достижением. 0 — не показывать.", "How many currently doable requirements to list under each tracked achievement. 0 — don't list." }, ["ShowAllRequirements"] = new string[4] { "Показывать все условия", "Show all requirements", "Выключено — под отслеживаемым достижением только условия, которые можно выполнить сейчас. Включено — все невыполненные, недоступные помечены «×».", "Off — only requirements you can complete right now are listed under a tracked achievement. On — all unfinished ones, unavailable marked with \"×\"." }, ["Opacity"] = new string[4] { "Непрозрачность фона (%)", "Background opacity (%)", "Насколько плотный фон у списка отслеживаемых достижений и навыков: 0 — полностью прозрачный, 100 — сплошной.", "How solid the tracked list background is: 0 — fully transparent, 100 — solid." } }; private static readonly Dictionary<string, string[]> Sections = new Dictionary<string, string[]> { ["General"] = new string[2] { "1. Общие", "1. General" }, ["Popups"] = new string[2] { "2. Всплывающие счётчики", "2. Progress popups" }, ["Tracker"] = new string[2] { "3. Отслеживание на экране", "3. On-screen tracker" } }; private static readonly List<KeyValuePair<ConfigDefinition, ConfigurationManagerAttributes>> Attributes = new List<KeyValuePair<ConfigDefinition, ConfigurationManagerAttributes>>(); public static ConfigDescription Describe(string section, string key, AcceptableValueBase range = null, bool hidden = false) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = -Attributes.Count, Browsable = (hidden ? new bool?(false) : ((bool?)null)) }; Attributes.Add(new KeyValuePair<ConfigDefinition, ConfigurationManagerAttributes>(new ConfigDefinition(section, key), configurationManagerAttributes)); Fill(section, key, configurationManagerAttributes, ru: false); return new ConfigDescription(configurationManagerAttributes.Description, range, new object[1] { configurationManagerAttributes }); } private static void Fill(string section, string key, ConfigurationManagerAttributes attrs, bool ru) { if (Texts.TryGetValue(key, out var value)) { attrs.DispName = (ru ? value[0] : value[1]); attrs.Description = (ru ? value[2] : value[3]); } if (Sections.TryGetValue(section, out var value2)) { attrs.Category = (ru ? value2[0] : value2[1]); } } public static void Apply() { bool ru = Loc.Ru; foreach (KeyValuePair<ConfigDefinition, ConfigurationManagerAttributes> attribute in Attributes) { Fill(attribute.Key.Section, attribute.Key.Key, attribute.Value, ru); } foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if ((Object)(object)((value != null) ? value.Instance : null) == (Object)null || value.Metadata.GUID.IndexOf("configurationmanager", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo methodInfo = AccessTools.Method(((object)value.Instance).GetType(), "BuildSettingList", (Type[])null, (Type[])null); if (!(methodInfo == null) && methodInfo.GetParameters().Length == 0) { try { methodInfo.Invoke(value.Instance, null); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not refresh " + value.Metadata.GUID + ": " + ex.Message)); } } } } } internal sealed class StarInfo : MonoBehaviour { public string Text = ""; } [HarmonyPatch] internal static class CraftMarks { private const string StarName = "AchievementTrackerStar"; private static readonly Dictionary<string, List<string>> PendingPieces = new Dictionary<string, List<string>>(); private static readonly Dictionary<string, List<string>> PendingItems = new Dictionary<string, List<string>>(); private static readonly Dictionary<string, List<string>> PendingInventory = new Dictionary<string, List<string>>(); private static readonly FieldRef<InventoryGrid, List<InventoryElement>> ElementsRef = AccessTools.FieldRefAccess<InventoryGrid, List<InventoryElement>>("m_elements"); private static float s_nextRebuild; private static Sprite s_star; private static FieldInfo s_pieceIconsField; private static FieldInfo s_iconField; private static bool s_broken; private static readonly Dictionary<string, List<string>> CookedFrom = new Dictionary<string, List<string>>(); private static readonly Dictionary<string, List<string>> PlantSeeds = new Dictionary<string, List<string>>(); private static ZNetScene s_sourcesFor; private static readonly FieldRef<BuildUiPieceButton, Image> BuildButtonIconRef = AccessTools.FieldRefAccess<BuildUiPieceButton, Image>("m_icon"); private static readonly FieldRef<BuildUi, List<BuildUiPieceButton>> BuildButtonsRef = AccessTools.FieldRefAccess<BuildUi, List<BuildUiPieceButton>>("m_pieceButtons"); private static float s_nextBuildRefresh; private static readonly Dictionary<int, List<string>> MarkedCells = new Dictionary<int, List<string>>(); private static float s_nextRecipeRefresh; public static void MarkDirty() { s_nextRebuild = 0f; } private static void Add(Dictionary<string, List<string>> map, string key, string line) { if (!map.TryGetValue(key, out var value)) { value = (map[key] = new List<string>()); } if (!value.Contains(line)) { value.Add(line); } } private static string Action(ReqKind kind) { return kind switch { ReqKind.PiecePlaced => Loc.S("построить", "build"), ReqKind.ItemCraft => Loc.S("создать", "craft"), ReqKind.ItemPickup => Loc.S("взять в свой инвентарь", "take into your inventory"), ReqKind.FoodEaten => Loc.S("съесть", "eat"), _ => Loc.S("поймать или собрать самому", "catch or gather yourself"), }; } private static void EnsurePending() { if (Time.time < s_nextRebuild) { return; } s_nextRebuild = Time.time + 1f; PendingPieces.Clear(); PendingItems.Clear(); PendingInventory.Clear(); if (!Analyzer.Ready) { return; } foreach (Achievement item in Analyzer.All()) { if (item.m_unlocked) { continue; } PlayerStats s = Analyzer.StatsFor(item); string text = Names.Achievement(item); foreach (Requirement requirement in Analyzer.GetRequirements(item)) { Dictionary<string, List<string>> map; switch (requirement.Kind) { case ReqKind.PiecePlaced: map = PendingPieces; break; case ReqKind.ItemCraft: map = PendingItems; break; case ReqKind.ItemPickup: case ReqKind.Pickable: case ReqKind.FoodEaten: map = PendingInventory; break; default: continue; } float value; bool has = Analyzer.TryGetValue(requirement, s, out value); if (!Analyzer.IsMet(requirement, has, value)) { string key = ((requirement.Kind == ReqKind.Pickable) ? Names.PickableItem(requirement.Key) : requirement.Key); Add(map, key, text + ": " + Action(requirement.Kind)); } } } EnsureSources(); foreach (KeyValuePair<string, List<string>> item2 in CookedFrom) { if (!PendingItems.TryGetValue(item2.Key, out var value2)) { continue; } string text2 = Names.L(item2.Key); foreach (string item3 in item2.Value) { foreach (string item4 in value2) { string text3 = item4.Substring(0, item4.LastIndexOf(':')); Add(PendingInventory, item3, text3 + ": " + Loc.S("приготовить «" + text2 + "»", "cook \"" + text2 + "\"")); } } } foreach (KeyValuePair<string, List<string>> plantSeed in PlantSeeds) { if (!PendingPieces.TryGetValue(plantSeed.Key, out var value3)) { continue; } string text4 = Names.L(plantSeed.Key); foreach (string item5 in plantSeed.Value) { foreach (string item6 in value3) { string text5 = item6.Substring(0, item6.LastIndexOf(':')); Add(PendingInventory, item5, text5 + ": " + Loc.S("посадить «" + text4 + "»", "plant \"" + text4 + "\"")); } } } } private static string Tooltip(List<string> lines) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (lines == null || lines.Count == 0) { return ""; } return "<color=#" + UiKit.Hex(UiKit.Orange) + "><b>" + Loc.S("Нужно для достижений:", "Needed for achievements:") + "</b></color>\n" + string.Join("\n", lines.Select((string l) => "• " + l)); } private static void AddTo(Dictionary<string, List<string>> map, string key, string value) { if (!map.TryGetValue(key, out var value2)) { value2 = (map[key] = new List<string>()); } if (!value2.Contains(value)) { value2.Add(value); } } private static void EnsureSources() { if (((Object)(object)s_sourcesFor != (Object)null && (Object)(object)s_sourcesFor == (Object)(object)ZNetScene.instance) || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } s_sourcesFor = ZNetScene.instance; CookedFrom.Clear(); PlantSeeds.Clear(); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { CookingStation val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CookingStation>() : null); if ((Object)(object)val == (Object)null) { continue; } foreach (ItemConversion item in val.m_conversion) { if (!((Object)(object)item?.m_from == (Object)null) && !((Object)(object)item.m_to == (Object)null)) { AddTo(CookedFrom, item.m_to.m_itemData.m_shared.m_name, item.m_from.m_itemData.m_shared.m_name); } } } foreach (GameObject item2 in ObjectDB.instance.m_items) { PieceTable val2 = ((!((Object)(object)item2 != (Object)null)) ? null : item2.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_buildPieces); if ((Object)(object)val2 == (Object)null) { continue; } foreach (GameObject piece in val2.m_pieces) { Piece val3 = (((Object)(object)piece != (Object)null) ? piece.GetComponent<Piece>() : null); if ((Object)(object)val3 == (Object)null || (Object)(object)piece.GetComponent<Plant>() == (Object)null) { continue; } Requirement[] resources = val3.m_resources; foreach (Requirement val4 in resources) { if ((Object)(object)val4?.m_resItem != (Object)null) { AddTo(PlantSeeds, val3.m_name, val4.m_resItem.m_itemData.m_shared.m_name); } } } } } private static void SetStar(Transform icon, List<string> why, bool topLeft = false) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) Transform val = icon.Find("AchievementTrackerStar"); if (why == null) { if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } return; } if ((Object)(object)val == (Object)null) { RectTransform obj = UiKit.Rect("AchievementTrackerStar", icon); Vector2 anchorMin = (obj.anchorMax = (topLeft ? new Vector2(0f, 1f) : new Vector2(1f, 1f))); obj.anchorMin = anchorMin; obj.pivot = (topLeft ? new Vector2(0f, 1f) : new Vector2(1f, 1f)); obj.anchoredPosition = (topLeft ? new Vector2(-4f, 4f) : new Vector2(4f, 4f)); obj.sizeDelta = new Vector2(16f, 16f); Image obj2 = ((Component)obj).gameObject.AddComponent<Image>(); obj2.sprite = StarSprite(); ((Graphic)obj2).color = new Color(1f, 0.85f, 0.2f); ((Graphic)obj2).raycastTarget = false; Outline obj3 = ((Component)obj).gameObject.AddComponent<Outline>(); ((Shadow)obj3).effectColor = new Color(0f, 0f, 0f, 0.9f); ((Shadow)obj3).effectDistance = new Vector2(1f, -1f); ((Component)obj).gameObject.AddComponent<StarInfo>(); val = (Transform)(object)obj; } ((Component)val).GetComponent<StarInfo>().Text = Tooltip(why); ((Component)val).gameObject.SetActive(true); val.SetAsLastSibling(); } private static Sprite StarSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_0197: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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) if ((Object)(object)s_star != (Object)null) { return s_star; } Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; Vector2[] array = (Vector2[])(object)new Vector2[10]; for (int i = 0; i < 10; i++) { float num = (float)Math.PI / 2f + (float)i * (float)Math.PI / 5f; float num2 = ((i % 2 == 0) ? 0.48f : 0.2f) * 64f; array[i] = new Vector2(32f + Mathf.Cos(num) * num2, 32f + Mathf.Sin(num) * num2 - 1.92f); } Color32[] array2 = (Color32[])(object)new Color32[4096]; for (int j = 0; j < 64; j++) { for (int k = 0; k < 64; k++) { int num3 = 0; for (int l = 0; l < 4; l++) { for (int m = 0; m < 4; m++) { if (Inside(array, new Vector2((float)k + ((float)m + 0.5f) / 4f, (float)j + ((float)l + 0.5f) / 4f))) { num3++; } } } array2[j * 64 + k] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num3 * 255 / 16)); } } val.SetPixels32(array2); val.Apply(); s_star = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f); return s_star; } private static bool Inside(Vector2[] poly, Vector2 p) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) bool flag = false; int num = 0; int num2 = poly.Length - 1; while (num < poly.Length) { if (poly[num].y > p.y != poly[num2].y > p.y && p.x < (poly[num2].x - poly[num].x) * (p.y - poly[num].y) / (poly[num2].y - poly[num].y) + poly[num].x) { flag = !flag; } num2 = num++; } return flag; } private static List<string> Why(Dictionary<string, List<string>> map, string key) { if (!Plugin.MarkUncrafted.Value || key == null || !map.TryGetValue(key, out var value)) { return null; } return value; } [HarmonyPostfix] [HarmonyPatch(typeof(Hud), "UpdatePieceList")] private static void HudUpdatePieceList(Hud __instance, Player player) { if (s_broken) { return; } try { if (Plugin.MarkUncrafted.Value) { EnsurePending(); } s_pieceIconsField = s_pieceIconsField ?? AccessTools.Field(typeof(Hud), "m_pieceIcons"); if (!(s_pieceIconsField?.GetValue(__instance) is IList list)) { return; } List<Piece> buildPieces = player.GetBuildPieces(); for (int i = 0; i < list.Count; i++) { object obj = list[i]; s_iconField = s_iconField ?? AccessTools.Field(obj.GetType(), "m_icon"); object? obj2 = s_iconField?.GetValue(obj); Image val = (Image)((obj2 is Image) ? obj2 : null); if (val != null && !((Object)(object)val == (Object)null)) { SetStar(((Component)val).transform, (i < buildPieces.Count) ? Why(PendingPieces, buildPieces[i]?.m_name) : null); } } } catch (Exception ex) { s_broken = true; Plugin.Log.LogError((object)("Build menu stars disabled until restart: " + ex)); } } private static void MarkBuildButton(BuildUiPieceButton button) { if (s_broken || (Object)(object)button == (Object)null) { return; } try { Image val = BuildButtonIconRef.Invoke(button); if (!((Object)(object)val == (Object)null)) { if (Plugin.MarkUncrafted.Value) { EnsurePending(); } SetStar(((Component)val).transform, Why(PendingPieces, button.Piece?.m_name), topLeft: true); } } catch (Exception ex) { s_broken = true; Plugin.Log.LogError((object)("Build menu stars disabled until restart: " + ex)); } } [HarmonyPostfix] [HarmonyPatch(typeof(BuildUiPieceButton), "Setup")] private static void BuildButtonSetup(BuildUiPieceButton __instance) { MarkBuildButton(__instance); } [HarmonyPostfix] [HarmonyPatch(typeof(BuildUi), "Update")] private static void BuildUiUpdate(BuildUi __instance) { if (s_broken || Time.time < s_nextBuildRefresh) { return; } s_nextBuildRefresh = Time.time + 0.5f; List<BuildUiPieceButton> list = BuildButtonsRef.Invoke(__instance); if (list == null) { return; } foreach (BuildUiPieceButton item in list) { MarkBuildButton(item); } } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] private static void InventoryGridUpdateGui(InventoryGrid __instance) { if (s_broken) { return; } try { List<InventoryElement> list = ElementsRef.Invoke(__instance); Inventory inventory = __instance.GetInventory(); if (list == null || inventory == null) { return; } if (Plugin.MarkUncrafted.Value) { EnsurePending(); } int width = inventory.GetWidth(); MarkedCells.Clear(); foreach (ItemData allItem in inventory.GetAllItems()) { List<string> list2 = Why(PendingInventory, allItem.m_shared.m_name); if (list2 != null) { int num = allItem.m_gridPos.y * width + allItem.m_gridPos.x; if (num >= 0 && num < list.Count) { MarkedCells[num] = list2; } } } for (int i = 0; i < list.Count; i++) { if (!((Object)(object)list[i] == (Object)null) && !((Object)(object)list[i].m_icon == (Object)null)) { SetStar(((Component)list[i].m_icon).transform, MarkedCells.TryGetValue(i, out var value) ? value : null); } } } catch (Exception ex) { s_broken = true; Plugin.Log.LogError((object)("Inventory stars disabled until restart: " + ex)); } } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGui), "Update")] private static void InventoryGuiUpdate(InventoryGui __instance) { if (s_broken || Time.time < s_nextRecipeRefresh || !InventoryGui.IsVisible()) { return; } s_nextRecipeRefresh = Time.time + 0.5f; try { if (Plugin.MarkUncrafted.Value) { EnsurePending(); } if (!(Traverse.Create((object)__instance).Field("m_availableRecipes").GetValue() is IList list)) { return; } foreach (object item in list) { Traverse val = Traverse.Create(item); object value = val.Property("InterfaceElement", (object[])null).GetValue(); GameObject val2 = (GameObject)((value is GameObject) ? value : null); Transform val3 = (((Object)(object)val2 != (Object)null) ? val2.transform.Find("icon") : null); if (!((Object)(object)val3 == (Object)null)) { object value2 = val.Property("Recipe", (object[])null).GetValue(); Recipe val4 = (Recipe)((value2 is Recipe) ? value2 : null); string key = ((val.Property("ItemData", (object[])null).GetValue() is ItemData) ? null : val4?.m_item?.m_itemData?.m_shared?.m_name); SetStar(val3, Why(PendingItems, key)); } } } catch (Exception ex) { s_broken = true; Plugin.Log.LogError((object)("Crafting stars disabled until restart: " + ex)); } } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGui), "AddRecipeToList")] private static void AddRecipeToList(InventoryGui __instance, Recipe recipe, ItemData item) { if (s_broken) { return; } try { if (Plugin.MarkUncrafted.Value) { EnsurePending(); } if (Traverse.Create((object)__instance).Field("m_availableRecipes").GetValue() is IList { Count: not 0 } list) { object value = Traverse.Create(list[list.Count - 1]).Property("InterfaceElement", (object[])null).GetValue(); GameObject val = (GameObject)((value is GameObject) ? value : null); Transform val2 = (((Object)(object)val != (Object)null) ? val.transform.Find("icon") : null); if (!((Object)(object)val2 == (Object)null)) { string key = ((item != null) ? null : recipe?.m_item?.m_itemData?.m_shared?.m_name); SetStar(val2, Why(PendingItems, key)); } } } catch (Exception ex) { s_broken = true; Plugin.Log.LogError((object)("Crafting stars disabled until restart: " + ex)); } } } internal static class GameText { private static readonly char[] EndChars = " (){}[]+-!?/\\&%,.:-=<>\n".ToCharArray(); private static readonly Dictionary<string, Dictionary<string, string>> Tables = new Dictionary<string, Dictionary<string, string>>(); private static string TargetLanguage() { if (Plugin.Language.Value == UiLanguage.Game) { return null; } string text = ((Plugin.Language.Value == UiLanguage.Russian) ? "Russian" : "English"); Localization instance = Localization.instance; if (!(((instance != null) ? instance.GetSelectedLanguage() : null) == text)) { return text; } return null; } public static string Localize(string text) { return LocalizeIn(text, TargetLanguage()); } public static string LocalizeIn(string text, string language) { Localization instance = Localization.instance; if (instance == null || string.IsNullOrEmpty(text)) { return text; } string text2 = ((language != null && instance.GetSelectedLanguage() != language) ? language : null); Dictionary<string, string> dictionary = ((text2 != null) ? GetTable(text2) : null); if (dictionary == null || text.IndexOf('$') < 0) { return instance.Localize(text); } StringBuilder stringBuilder = new StringBuilder(); int num = 0; while (num < text.Length) { int num2 = text.IndexOf('$', num); if (num2 < 0) { break; } int num3 = text.IndexOfAny(EndChars, num2); if (num3 < 0) { num3 = text.Length; } string text3 = text.Substring(num2 + 1, num3 - num2 - 1); stringBuilder.Append(text, num, num2 - num); stringBuilder.Append(dictionary.TryGetValue(text3, out var value) ? value : instance.Localize("$" + text3)); num = num3; } stringBuilder.Append(text, num, text.Length - num); return stringBuilder.ToString(); } private static Dictionary<string, string> GetTable(string language) { if (Tables.TryGetValue(language, out var value)) { return value; } Dictionary<string, string> dictionary = null; try { dictionary = Load(language); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to load game strings for " + language + ", using game language: " + ex.Message)); } Tables[language] = dictionary; return dictionary; } private static Dictionary<string, string> Load(string language) { //IL_002d: 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_0050: Expected O, but got Unknown Dictionary<string, string> dictionary = new Dictionary<string, string>(); LocalizationSettings val = (LocalizationSettings)AccessTools.Field(typeof(Localization), "m_localizationSettings").GetValue(null); MethodInfo methodInfo = AccessTools.Method(typeof(Localization), "DoQuoteLineSplit", (Type[])null, (Type[])null); if ((Object)val == (Object)null || methodInfo == null) { throw new InvalidOperationException("localization internals not found"); } foreach (TextAsset localization in val.Localizations) { if ((Object)(object)localization == (Object)null) { continue; } StringReader stringReader = new StringReader(localization.text); int num = Array.FindIndex(stringReader.ReadLine().Split(new char[1] { ',' }), (string h) => h.Trim().Trim(new char[1] { '"' }) == language); if (num < 0) { continue; } foreach (List<string> item in (List<List<string>>)methodInfo.Invoke(Localization.instance, new object[1] { stringReader })) { if (item.Count == 0 || item.Count <= num) { continue; } string text = item[0]; if (text.Length != 0 && !text.StartsWith("//")) { string text2 = item[num].Trim(); if (string.IsNullOrEmpty(text2) || text2[0] == '\r') { text2 = ((item.Count > 1) ? item[1].Trim() : ""); } if (text2.Length > 0) { dictionary[text] = text2; } } } } return dictionary; } } internal static class Guide { private static readonly Dictionary<string, string[]> Texts = new Dictionary<string, string[]> { ["AllBosses"] = new string[2] { "Убей всех главных боссов мира, сложность не важна. Засчитывается, если ты участвовал в убийстве. Боссов призывают на алтарях подношениями, а где искать алтари, подсказывают камни «{$piece_vegvisir}».", "Kill every main boss of the world, on any difficulty. It counts if you took part in the kill. Bosses are summoned at their altars with offerings; {$piece_vegvisir} stones show where the altars are." }, ["AllBossesHard"] = new string[2] { "То же, что «{ach:AllBosses}», но каждый босс должен быть убит при сложности боя «{$menu_modifier_hard}» или выше. Убийства на более лёгкой сложности сюда не идут — их придётся повторить.", "Same as {ach:AllBosses}, but each boss must be killed while the world's combat difficulty is {$menu_modifier_hard} or higher. Kills on easier settings don't count and have to be repeated." }, ["AllBossesNormal"] = new string[2] { "Убей всех главных боссов при сложности боя «{$menu_modifier_normal}» или выше. Убийства на сложности «{$menu_modifier_easy}», «{$menu_modifier_veryeasy}» и «{$menu_modifier_casual}» не засчитываются.", "Kill every main boss on {$menu_modifier_normal} combat difficulty or higher. Kills on {$menu_modifier_easy}, {$menu_modifier_veryeasy} or {$menu_modifier_casual} don't count." }, ["AllBuildPieces"] = new string[2] { "Поставь хотя бы по одному разу каждую постройку из всех инструментов: «{$item_hammer}», «{$item_hoe}» и «{$item_cultivator}» (посадки саженцев и семян тоже считаются). Большая часть открывается по мере того, как находишь новые материалы.", "Place every build piece at least once, from all tools: {$item_hammer}, {$item_hoe} and {$item_cultivator} (planting saplings and seeds counts too). Most of them unlock as you find new materials." }, ["AllFoodCooked"] = new string[2] { "Приготовь каждое блюдо хотя бы раз: на огне и на кухонных станциях вроде «{$piece_cauldron}» и «{$piece_oven}». Промежуточные заготовки (например, тесто или сырые пироги) тоже входят в список.", "Cook every dish at least once: on the fire and at kitchen stations like the {$piece_cauldron} and {$piece_oven}. Intermediate items like dough or uncooked pies are on the list too." }, ["AllFoodEaten"] = new string[2] { "Съешь хотя бы раз каждый съедобный предмет — и готовые блюда, и сырые продукты вроде ягод, грибов и предмета «{$item_honey}».", "Eat every edible item at least once — both cooked dishes and raw food like berries, mushrooms and {$item_honey}." }, ["AllItemCraft"] = new string[2] { "Создай на станциях каждый предмет, у которого есть рецепт: оружие, броню, инструменты, боеприпасы, материалы и прочее. Улучшение уже созданного предмета не считается новым крафтом.", "Craft every item that has a station recipe: weapons, armor, tools, ammo, materials and more. Upgrading an item you already made doesn't count as a new craft." }, ["AllMiniBosses"] = new string[2] { "Победи всех мини-боссов: {$enemy_skeletonfire}, {$enemy_fenringcultist_hildir}, {$enemy_goblinbrute_hildircombined} и {$enemy_charred_melee_Dyrnwyn}. Первые трое охраняют сундуки из заданий торговки {$npc_hildir} — их логова появляются на карте, когда берёшь задание.", "Defeat all minibosses: {$enemy_skeletonfire}, {$enemy_fenringcultist_hildir}, {$enemy_goblinbrute_hildircombined} and {$enemy_charred_melee_Dyrnwyn}. The first three guard the chests from {$npc_hildir}'s quests — their lairs appear on the map when you take the quest." }, ["AllWeaponCraft"] = new string[2] { "Создай каждое оружие, включая все варианты одного оружия с разными эффектами (например, алебарды с разной магией). Многие варианты требуют материалов из поздних биомов.", "Craft every weapon, including every variant of the same weapon with different effects (for example atgeirs with different magic). Many variants need late-biome materials." }, ["Arrived"] = new string[2] { "Просто появись в мире — засчитывается при первом входе.", "Just spawn into a world — it counts on your first entry." }, ["BigFish"] = new string[2] { "Поймай предметом «{$item_fishingrod}» рыбу с четырьмя звёздами (качество 4). Рыба, пойманная руками, не считается.", "Catch a four-star fish (quality 4) with a {$item_fishingrod}. Fish grabbed by hand don't count." }, ["Boss1Eikthyr"] = new string[2] { "Убей босса «{$enemy_eikthyr}». Алтарь находится в биоме «{$biome_meadows}», для призыва нужны два предмета «{$item_trophy_deer}».", "Kill {$enemy_eikthyr}. The altar is in the {$biome_meadows} biome; summon with {$item_trophy_deer} ×2." }, ["Boss2Elder"] = new string[2] { "Убей босса «{$enemy_gdking}». Алтарь находится в биоме «{$biome_blackforest}», для призыва нужны три предмета «{$item_ancientseed}», оно выпадает при разрушении объекта «{$enemy_greydwarfspawner}».", "Kill {$enemy_gdking}. The altar is in the {$biome_blackforest} biome; summon with {$item_ancientseed} ×3 — it drops when you destroy a {$enemy_greydwarfspawner}." }, ["Boss3Bonemass"] = new string[2] { "Убей босса «{$enemy_bonemass}». Алтарь находится в биоме «{$biome_swamp}», для призыва нужны десять предметов «{$item_witheredbone}» из подземелий «{$location_sunkencrypt}» (вход открывает «{$item_cryptkey}»). Босс устойчив к рубящему и колющему урону — лучше всего работает дробящее оружие.", "Kill {$enemy_bonemass}. The altar is in the {$biome_swamp} biome; summon with {$item_witheredbone} ×10 from the {$location_sunkencrypt} (opened with a {$item_cryptkey}). It resists slash and pierce — blunt weapons work best." }, ["Boss4Moder"] = new string[2] { "Убей босса «{$enemy_dragon}». Алтарь находится в биоме «{$biome_mountain}», для призыва нужны три предмета «{$item_dragonegg}».", "Kill {$enemy_dragon}. The altar is in the {$biome_mountain} biome; summon with {$item_dragonegg} ×3." }, ["Boss5Yagluth"] = new string[2] { "Убей босса «{$enemy_goblinking}». Алтарь находится в биоме «{$biome_plains}», для призыва нужны пять предметов «{$item_goblintotem}».", "Kill {$enemy_goblinking}. The altar is in the {$biome_plains} biome; summon with {$item_goblintotem} ×5." }, ["Boss6Queen"] = new string[2] { "Убей босса «{$enemy_seekerqueen}» — она в локации «{$location_dvergrboss}» ({$biome_mistlands}). Вход открывает «{$item_dvergrkey}», его собирают из нескольких предметов «{$item_dvergrkeyfragment}», которые находят в локациях «{$location_dvergrtown}».", "Kill {$enemy_seekerqueen} in the {$location_dvergrboss} ({$biome_mistlands} biome). The entrance opens with a {$item_dvergrkey}, assembled from several {$item_dvergrkeyfragment} items found in {$location_dvergrtown} locations." }, ["Boss7Fader"] = new string[2] { "Убей босса «{$enemy_fader}» ({$biome_ashlands}). Для призыва нужно несколько предметов «{$item_bellfragment}».", "Kill {$enemy_fader} in the {$biome_ashlands} biome. Summoning needs several {$item_bellfragment} items." }, ["Boss8FrozenKing"] = new string[2] { "Убей босса «{$enemy_frozenking}» в биоме «{$biome_deepnorth}» (засчитывается последняя фаза боя).", "Kill {$enemy_frozenking} in the {$biome_deepnorth} biome (the final phase of the fight counts)." }, ["BuildDyrnwyn"] = new string[2] { "Собери меч «{$item_sword_dyrnwyn}» из трёх частей: «{$item_Dyrnwyn_blade}», «{$item_Dyrnwyn_hilt}» и «{$item_Dyrnwyn_tip}» ({$biome_ashlands}). Засчитывается сам крафт меча.", "Assemble {$item_sword_dyrnwyn} from three parts: {$item_Dyrnwyn_blade}, {$item_Dyrnwyn_hilt} and {$item_Dyrnwyn_tip} ({$biome_ashlands} biome). Crafting the sword is what counts." }, ["BuildHigh"] = new string[2] { "Поставь постройку на высоте не меньше 49 м над землёй прямо под ней. Засчитываются только детали на твоём собственном фундаменте — строй высокую башню от земли.", "Place a piece at least 49 m above the ground directly below it. Only pieces on your own foundation count — build a tall tower from the ground up." }, ["BuildHighWorld"] = new string[2] { "Поставь постройку на абсолютной высоте больше 432 м (около 400 м над уровнем моря). Проще всего — на вершине высокой горы. Деталь должна стоять на твоём фундаменте и не внутри подземелья.", "Place a piece at a world height above 432 m (about 400 m above sea level). A tall mountain peak is easiest. It must stand on your own foundation and not inside a dungeon." }, ["BuildHouse"] = new string[2] { "Построй дом, в котором есть все нужные категории деталей: полы, стены, крыша, мебель, освещение и так далее. Точные количества по каждой категории — в условиях ниже.", "Build a house containing all required piece categories: floors, walls, roof, furniture, lighting and so on. Exact counts per category are listed in the requirements below." }, ["BuildVillage"] = new string[2] { "Как «{ach:BuildHouse}», но в масштабе деревни: много зданий, полов, стен, крыш и архитектурных элементов. Точные количества — в условиях ниже.", "Like {ach:BuildHouse}, but village-sized: many buildings, floors, walls, roofs and architecture pieces. Exact counts are listed in the requirements below." }, ["ChildOfOdin"] = new string[2] { "Мета-достижение: получи «{ach:ExploreNSEWNoMap}», «{ach:AllBossesHard}», «{ach:KillAllCreaturesHard}» и «{ach:GrindDays}».", "Meta achievement: unlock {ach:ExploreNSEWNoMap}, {ach:AllBossesHard}, {ach:KillAllCreaturesHard} and {ach:GrindDays}." }, ["DeathByAllTypes"] = new string[2] { "Погибни от каждой причины из списка ниже: враг, падение, утопление, огонь, холод, яд, дым, край мира и другие. Засчитывается только тот урон, который тебя добил.", "Die from every cause in the list below: enemy, falling, drowning, fire, freezing, poison, smoke, the world's edge and more. Only the damage that actually kills you counts." }, ["DeathByObliterator"] = new string[2] { "Погибни внутри постройки «{$piece_incinerator}», когда она срабатывает.", "Die inside the {$piece_incinerator} when it activates." }, ["DeathByTree"] = new string[2] { "Погибни от падающего дерева или бревна.", "Get killed by a falling tree or log." }, ["DeathByTreeAll"] = new string[2] { "Погибни от каждого вида дерева из списка ниже. Удобнее всего с малым запасом здоровья, стоя там, куда падает ствол.", "Get killed by every kind of tree in the list below. Easiest with low health, standing where the trunk falls." }, ["ExploreNSEW"] = new string[2] { "Уйди дальше 10 350 м от центра мира на север, юг, восток и запад. Это у самого края мира, так что понадобится корабль. Сколько осталось в каждую сторону — в условиях ниже.", "Travel more than 10,350 m from the world centre to the north, south, east and west. That's right at the world's edge, so you'll need a ship. The distance left in each direction is shown in the requirements below." }, ["ExploreNSEWNoMap"] = new string[2] { "То же, что «{ach:ExploreNSEW}», но в мире с модификатором «{$menu_nomap}». Путешествия в обычном мире сюда не засчитываются.", "Same as {ach:ExploreNSEW}, but in a world with the {$menu_nomap} modifier. Travel in a normal world doesn't count." }, ["FindAllTrophies"] = new string[2] { "Подбери хотя бы по одному трофею каждого вида. Трофеи выпадают не всегда — иногда придётся убить нескольких существ. Где кого искать — в условиях ниже.", "Pick up at least one trophy of every kind. Creatures don't always drop them, so you may need several kills. Where to find each creature is shown in the requirements below." }, ["FirstDeath"] = new string[2] { "Погибни в первый раз.", "Die for the first time." }, ["GrindBuild"] = new string[2] { "Поставь 500 построек.", "Place 500 build pieces." }, ["GrindComfort"] = new string[2] { "Достигни уровня «{$se_rested_comfort}» 20. Его дают укрытие, огонь, кровать, стулья, ковры, баннеры и другие предметы рядом; из каждой группы учитывается только лучший предмет.", "Reach {$se_rested_comfort} level 20. It comes from shelter, fire, a bed, chairs, rugs, banners and other nearby items; only the best item from each group counts." }, ["GrindCook"] = new string[2] { "Приготовь 100 блюд в сумме, любых.", "Cook 100 dishes in total, any kind." }, ["GrindDays"] = new string[2] { "Проживи 30 игровых дней подряд без смерти. Смерть обнуляет счётчик, засчитывается лучший результат.", "Survive 30 in-game days in a row without dying. Death resets the counter; your best streak counts." }, ["GrindEat"] = new string[2] { "Съешь 500 единиц еды в сумме.", "Eat 500 food items in total." }, ["GrindFish"] = new string[2] { "Поймай каждый вид рыбы. Разные виды водятся в разных биомах и клюют на разную наживку.", "Catch every kind of fish. Different kinds live in different biomes and bite on different bait." }, ["GrindForager"] = new string[2] { "Собери 250 ягод и 250 грибов (два отдельных счётчика).", "Pick 250 berries and 250 mushrooms (two separate counters)." }, ["GrindHarvest"] = new string[2] { "Собери 500 единиц урожая с грядок.", "Harvest 500 crops from your fields." }, ["GrindHoney"] = new string[2] { "Собери 500 единиц предмета «{$item_honey}» из построек «{$piece_beehive}». Чтобы построить «{$piece_beehive}», нужна «{$item_queenbee}».", "Collect 500 {$item_honey} from a {$piece_beehive}. Building a {$piece_beehive} needs a {$item_queenbee}." }, ["GrindLeviathan"] = new string[2] { "Потревожь огромное морское существо, похожее на поросший ракушками остров посреди биома «{$biome_ocean}»: добывай на нём киркой, пока оно не уйдёт под воду.", "Disturb the huge sea creature that looks like a barnacle-covered island in the {$biome_ocean}: mine it with a pickaxe until it dives." }, ["GrindSail"] = new string[2] { "Проплыви в сумме около 132 км (дважды вокруг мира). Засчитывается и когда ты пассажир.", "Sail about 132 km in total (twice around the world). It counts as a passenger too." }, ["GrindSailHelm"] = new string[2] { "Проплыви около 66 км, стоя за штурвалом.", "Sail about 66 km while manning the helm." }, ["GrindTreasure"] = new string[2] { "Найди и раскопай 10 закопанных сундуков с сокровищами.", "Find and dig up 10 buried treasure chests." }, ["GrindTrees"] = new string[2] { "Сруби 500 деревьев. Засчитывается каждое срубленное дерево; разрубание упавших брёвен на части не считается.", "Cut down 500 trees. Every tree you cut down counts; chopping the fallen logs into pieces do