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 CraftPlanner v0.1.0
bin/Debug/CraftPlanner.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.0.0.0")] namespace CraftPlanner; internal static class CameraPatches { [HarmonyPatch(typeof(GameCamera), "LateUpdate")] private static class GameCameraLateUpdatePatch { private static bool Prefix() { return !PlannerUI.Visible; } } [HarmonyPatch(typeof(InventoryGui), "IsVisible")] private static class InventoryGuiVisiblePatch { private static void Postfix(ref bool __result) { if (PlannerUI.Visible) { __result = true; } } } } public sealed class RecipeEntry { public string Key; public string Display; public string SearchIndex = string.Empty; public string Station; public int StationLevel; public int OutputAmount = 1; public bool IsPiece; public Recipe Recipe; public Piece Piece; public Requirement[] Resources => IsPiece ? Piece.m_resources : Recipe.m_resources; } public sealed class GameData { private static GameData _cached; private static ObjectDB _cachedDb; public readonly Dictionary<string, RecipeEntry> ByKey = new Dictionary<string, RecipeEntry>(); public readonly Dictionary<string, string> DisplayNames = new Dictionary<string, string>(); public readonly List<RecipeEntry> All = new List<RecipeEntry>(); private Dictionary<string, string> _processing; private static FieldRef<ObjectDB, List<Piece>> _buildPiecesRef; private static bool _buildPiecesResolved; public Dictionary<string, string> Processing { get { if (_processing == null) { BuildProcessing(); } return _processing; } } public static GameData Get() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } if (_cached != null && (Object)(object)_cachedDb == (Object)(object)instance) { return _cached; } _cachedDb = instance; _cached = Build(instance); return _cached; } public string DisplayOf(string key) { if (string.IsNullOrEmpty(key)) { return "?"; } if (DisplayNames.TryGetValue(key, out var value)) { return value; } return Loc(key); } public static string Loc(string s) { if (string.IsNullOrEmpty(s)) { return s; } try { Localization instance = Localization.instance; if (instance != null) { return instance.Localize(s); } } catch { } return s; } private static GameData Build(ObjectDB odb) { GameData gameData = new GameData(); if (odb.m_items != null) { foreach (GameObject item2 in odb.m_items) { if ((Object)(object)item2 == (Object)null) { continue; } ItemDrop component = item2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { string name = component.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name) && !gameData.DisplayNames.ContainsKey(name)) { gameData.DisplayNames[name] = Loc(name); } } } } if (odb.m_recipes != null) { foreach (Recipe recipe in odb.m_recipes) { if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || recipe.m_resources == null || recipe.m_resources.Length == 0) { continue; } ItemDrop item = recipe.m_item; if ((Object)(object)item == (Object)null || item.m_itemData == null || item.m_itemData.m_shared == null) { continue; } string name2 = item.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name2)) { RecipeEntry recipeEntry = new RecipeEntry { Key = name2, Display = gameData.DisplayOf(name2), Station = (((Object)(object)recipe.m_craftingStation != (Object)null) ? Loc(recipe.m_craftingStation.m_name) : null), StationLevel = recipe.m_minStationLevel, OutputAmount = Mathf.Max(1, recipe.m_amount), IsPiece = false, Recipe = recipe }; recipeEntry.SearchIndex = BuildSearchIndex(gameData, name2, recipeEntry.Station); gameData.All.Add(recipeEntry); if (!gameData.ByKey.ContainsKey(name2)) { gameData.ByKey[name2] = recipeEntry; } } } } List<Piece> buildPieces = GetBuildPieces(odb); if (buildPieces != null) { foreach (Piece item3 in buildPieces) { if (!((Object)(object)item3 == (Object)null) && item3.m_resources != null && item3.m_resources.Length != 0 && !string.IsNullOrEmpty(item3.m_name)) { string name3 = item3.m_name; if (!gameData.ByKey.ContainsKey(name3)) { RecipeEntry recipeEntry2 = new RecipeEntry { Key = name3, Display = gameData.DisplayOf(name3), Station = null, StationLevel = 0, OutputAmount = 1, IsPiece = true, Piece = item3 }; recipeEntry2.SearchIndex = BuildSearchIndex(gameData, name3, null); gameData.All.Add(recipeEntry2); gameData.ByKey[name3] = recipeEntry2; } } } } gameData.All.Sort((RecipeEntry a, RecipeEntry b) => string.Compare(a.Display, b.Display, StringComparison.CurrentCultureIgnoreCase)); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Indexed " + gameData.All.Count + " recipes/pieces")); } return gameData; } private static string BuildSearchIndex(GameData data, string key, string station) { StringBuilder stringBuilder = new StringBuilder(); string value = data.DisplayOf(key); if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(value); } stringBuilder.Append(' ').Append(key.TrimStart('$').Replace('_', ' ')); if (!string.IsNullOrEmpty(station)) { stringBuilder.Append(' ').Append(station); } return stringBuilder.ToString().ToLowerInvariant(); } private static List<Piece> GetBuildPieces(ObjectDB odb) { if (!_buildPiecesResolved) { _buildPiecesResolved = true; try { _buildPiecesRef = AccessTools.FieldRefAccess<ObjectDB, List<Piece>>("m_buildPieces"); } catch (Exception) { _buildPiecesRef = null; } } if (_buildPiecesRef != null) { try { return _buildPiecesRef.Invoke(odb); } catch (Exception) { } } try { List<Piece> list = new List<Piece>(); Piece[] array = Resources.FindObjectsOfTypeAll<Piece>(); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null) && val.m_resources != null && val.m_resources.Length != 0 && !string.IsNullOrEmpty(val.m_name)) { list.Add(val); } } return list; } catch (Exception) { return null; } } private void BuildProcessing() { _processing = new Dictionary<string, string>(); ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Smelter component = prefab.GetComponent<Smelter>(); if ((Object)(object)component == (Object)null || component.m_conversion == null) { continue; } foreach (ItemConversion item in component.m_conversion) { if (item == null || (Object)(object)item.m_from == (Object)null || (Object)(object)item.m_to == (Object)null || item.m_from.m_itemData == null || item.m_from.m_itemData.m_shared == null || item.m_to.m_itemData == null || item.m_to.m_itemData.m_shared == null) { continue; } string name = item.m_from.m_itemData.m_shared.m_name; string name2 = item.m_to.m_itemData.m_shared.m_name; if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(name2) || name == name2) { continue; } if (_processing.TryGetValue(name2, out var value)) { if (value != name) { hashSet.Add(name2); } } else { _processing[name2] = name; } } } foreach (string item2 in hashSet) { _processing.Remove(item2); } } } public sealed class MatNode { public string Key; public string Display; public int Amount; public string Note; public readonly List<MatNode> Children = new List<MatNode>(); public bool IsRaw => Children.Count == 0; } public static class Materials { private const int MaxDepth = 16; public static MatNode Build(string key, int amount, int quality) { GameData gameData = GameData.Get(); if (gameData == null) { return null; } HashSet<string> path = new HashSet<string>(); return Expand(key, amount, quality, gameData, path, 0); } public static Dictionary<string, int> Totals(MatNode root) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (root == null) { return dictionary; } Collect(root, dictionary); return dictionary; } private static void Collect(MatNode node, Dictionary<string, int> totals) { if (node.IsRaw) { totals.TryGetValue(node.Key, out var value); totals[node.Key] = value + node.Amount; return; } foreach (MatNode child in node.Children) { Collect(child, totals); } } private static MatNode Expand(string key, int amount, int quality, GameData data, HashSet<string> path, int depth) { MatNode matNode = new MatNode { Key = key, Display = data.DisplayOf(key), Amount = amount }; if (amount <= 0) { return matNode; } if (depth >= 16 || path.Contains(key)) { return matNode; } if (data.ByKey.TryGetValue(key, out var value)) { Requirement[] resources = value.Resources; if (resources == null || resources.Length == 0) { return matNode; } int num = Math.Max(1, value.OutputAmount); int num2 = (amount + num - 1) / num; matNode.Note = (value.IsPiece ? "build" : ((value.Station != null) ? (value.Station + ((value.StationLevel > 1) ? (" lvl " + value.StationLevel) : "")) : "craft")); path.Add(key); Requirement[] array = resources; foreach (Requirement val in array) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null && val.m_resItem.m_itemData.m_shared != null) { int amount2 = val.GetAmount(quality); if (amount2 > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; MatNode item = Expand(name, amount2 * num2, quality, data, path, depth + 1); matNode.Children.Add(item); } } } path.Remove(key); return matNode; } if (CraftPlannerPlugin.DecomposeProcessing != null && CraftPlannerPlugin.DecomposeProcessing.Value && data.Processing.TryGetValue(key, out var value2)) { path.Add(key); MatNode matNode2 = Expand(value2, amount, quality, data, path, depth + 1); path.Remove(key); matNode2.Note = "smelted"; matNode.Children.Add(matNode2); } return matNode; } } internal sealed class PlannerUI { private const int WindowId = 202047470; private static readonly Color ColorOk = new Color(0.62f, 0.95f, 0.62f); private static readonly Color ColorMissing = new Color(1f, 0.78f, 0.42f); private static readonly Color ColorRaw = new Color(0.8f, 0.88f, 1f); private static readonly Color ColorNote = new Color(0.62f, 0.62f, 0.62f); private Rect _rect = new Rect(90f, 70f, 880f, 600f); private string _search = string.Empty; private List<RecipeEntry> _filtered = new List<RecipeEntry>(); private RecipeEntry _selected; private int _quality = 1; private int _maxQuality = 1; private int _target = 1; private Vector2 _listScroll; private Vector2 _detailScroll; private GameData _data; private MatNode _tree; private Dictionary<string, int> _totals = new Dictionary<string, int>(); private Dictionary<string, int> _have = new Dictionary<string, int>(); private bool _prevCursorVisible; private CursorLockMode _prevCursorLock; private bool _errorLogged; private GUIStyle _title; private GUIStyle _section; private GUIStyle _node; private GUIStyle _dim; public bool IsOpen { get; private set; } public static bool Visible { get; private set; } public void Toggle() { if (IsOpen) { Close(); } else { Open(); } } public void Open() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { Strings.Refresh(); _prevCursorVisible = Cursor.visible; _prevCursorLock = Cursor.lockState; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; IsOpen = true; Visible = true; Stopwatch stopwatch = Stopwatch.StartNew(); RefreshData(); Rescan(); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Planner opened in " + stopwatch.ElapsedMilliseconds + " ms")); } } } public void Close() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { IsOpen = false; Visible = false; Cursor.visible = _prevCursorVisible; Cursor.lockState = _prevCursorLock; } } public void Tick() { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } private void RefreshData() { try { _data = GameData.Get(); } catch (Exception ex) { CraftPlannerPlugin.Log.LogError((object)("Failed to index game data: " + ex)); _data = null; } ApplyFilter(); if (_selected != null) { _selected = ((_data != null && _data.ByKey.TryGetValue(_selected.Key, out var value)) ? value : null); } Rebuild(); } private void ApplyFilter() { _filtered.Clear(); if (_data == null) { return; } string text = (_search ?? string.Empty).Trim(); if (text.Length == 0) { _filtered.AddRange(_data.All); return; } foreach (RecipeEntry item in _data.All) { if (!string.IsNullOrEmpty(item.SearchIndex) && item.SearchIndex.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { _filtered.Add(item); } } } private void Select(RecipeEntry entry) { //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) _selected = entry; _quality = 1; _maxQuality = MaxQuality(entry); _detailScroll = Vector2.zero; Rebuild(); } private static int MaxQuality(RecipeEntry entry) { if (entry == null || entry.IsPiece) { return 1; } if ((Object)(object)entry.Recipe == (Object)null || (Object)(object)entry.Recipe.m_item == (Object)null) { return 1; } SharedData val = ((entry.Recipe.m_item.m_itemData != null) ? entry.Recipe.m_item.m_itemData.m_shared : null); return (val == null) ? 1 : Mathf.Max(1, val.m_maxQuality); } private void Rebuild() { if (_selected == null || _data == null) { _tree = null; _totals = new Dictionary<string, int>(); } else { _tree = Materials.Build(_selected.Key, Mathf.Max(1, _target), _quality); _totals = Materials.Totals(_tree); } } private void Rescan() { if ((Object)(object)Player.m_localPlayer == (Object)null) { _have = new Dictionary<string, int>(); } else { _have = StorageScanner.Scan(Mathf.Max(1f, CraftPlannerPlugin.ScanRadius.Value)); } } public void Draw() { //IL_0023: 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_0052: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { return; } try { InitStyles(); _rect = GUILayout.Window(202047470, _rect, new WindowFunction(DrawWindow), Strings.T("Title") + " v0.1.0", Array.Empty<GUILayoutOption>()); } catch (Exception ex) { if (!_errorLogged) { _errorLogged = true; CraftPlannerPlugin.Log.LogError((object)("Planner UI error: " + ex)); } Close(); } } public void Prewarm() { try { InitStyles(); } catch { } } private void DrawWindow(int id) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (_data == null && (Object)(object)Player.m_localPlayer != (Object)null) { RefreshData(); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DrawList(); GUILayout.Space(8f); DrawDetails(); GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private void DrawList() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_011f: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label(Strings.T("Search"), _dim, Array.Empty<GUILayoutOption>()); string text = GUILayout.TextField(_search ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }); if (text != _search) { _search = text; ApplyFilter(); } GUILayout.Space(6f); _listScroll = GUILayout.BeginScrollView(_listScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_data == null) { GUILayout.Label(Strings.T("LoadWorld"), _node, Array.Empty<GUILayoutOption>()); } else { foreach (RecipeEntry item in _filtered) { bool flag = _selected != null && _selected.Key == item.Key; Color backgroundColor = GUI.backgroundColor; if (flag) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(item.Display, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { Select(item); } GUI.backgroundColor = backgroundColor; } if (_filtered.Count == 0) { GUILayout.Label(Strings.T("NothingFound"), _dim, Array.Empty<GUILayoutOption>()); } } GUILayout.EndScrollView(); GUILayout.Label(Strings.T("Close") + " • " + _filtered.Count + " " + Strings.T("Entries"), _dim, Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); } private void DrawDetails() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //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_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); _detailScroll = GUILayout.BeginScrollView(_detailScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_selected == null) { GUILayout.Label(Strings.T("Pick"), _node, Array.Empty<GUILayoutOption>()); } else { GUILayout.Label(_selected.Display, _title, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(_selected.Station)) { GUILayout.Label(Strings.T("Station") + " " + _selected.Station + ((_selected.StationLevel > 1) ? (" lvl " + _selected.StationLevel) : string.Empty), _dim, Array.Empty<GUILayoutOption>()); } if (!_selected.IsPiece && _selected.OutputAmount > 1) { GUILayout.Label(Strings.Format("Output", _selected.OutputAmount), _dim, Array.Empty<GUILayoutOption>()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Target"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _target = AmountButton(1, _target); _target = AmountButton(5, _target); _target = AmountButton(10, _target); _target = AmountButton(25, _target); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (_maxQuality > 1) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Quality"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); for (int i = 1; i <= _maxQuality; i++) { Color backgroundColor = GUI.backgroundColor; if (i == _quality) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(i.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) }) && i != _quality) { _quality = i; Rebuild(); } GUI.backgroundColor = backgroundColor; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(6f); GUILayout.Label(Strings.T("Ingredients"), _section, Array.Empty<GUILayoutOption>()); if (_tree != null) { DrawNode(_tree, 0); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Raw"), _section, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button(Strings.T("Rescan"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) })) { Rescan(); } GUILayout.EndHorizontal(); DrawTotals(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private int AmountButton(int value, int current) { //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_001e: 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) Color backgroundColor = GUI.backgroundColor; if (current == value) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } int num = (GUILayout.Button("×" + value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) }) ? value : current); GUI.backgroundColor = backgroundColor; if (num != current) { Rebuild(); } return num; } private void DrawNode(MatNode node, int depth) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Space((float)depth * 18f); if (node.IsRaw) { GUI.color = ColorRaw; } else { GUI.color = Color.white; } GUILayout.Label(node.Display + " ×" + node.Amount, _node, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(node.Note)) { GUI.color = ColorNote; GUILayout.Label("[" + NoteName(node.Note) + "]", _dim, Array.Empty<GUILayoutOption>()); } GUI.color = Color.white; if (node.IsRaw) { _have.TryGetValue(node.Key, out var value); GUI.color = ((value >= node.Amount) ? ColorOk : ColorMissing); GUILayout.Label(Strings.T("Have") + " " + value, _dim, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); foreach (MatNode child in node.Children) { DrawNode(child, depth + 1); } } private static string NoteName(string note) { return note switch { "build" => Strings.T("Build"), "craft" => Strings.T("Craft"), "smelted" => Strings.T("Smelted"), _ => note, }; } private void DrawTotals() { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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) if (_totals == null || _totals.Count == 0) { GUILayout.Label(Strings.T("NoMaterials"), _dim, Array.Empty<GUILayoutOption>()); return; } List<string> list = new List<string>(_totals.Keys); list.Sort((string a, string b) => string.Compare(_data.DisplayOf(a), _data.DisplayOf(b), StringComparison.CurrentCultureIgnoreCase)); foreach (string item in list) { int num = _totals[item]; _have.TryGetValue(item, out var value); GUI.color = ((value >= num) ? ColorOk : ColorMissing); GUILayout.Label(_data.DisplayOf(item) + " " + value + " / " + num, _node, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } } private void InitStyles() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0044: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //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) //IL_007d: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown if (_title == null) { _title = new GUIStyle(GUI.skin.label) { fontSize = 19, fontStyle = (FontStyle)1 }; _section = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)1 }; _node = new GUIStyle(GUI.skin.label) { fontSize = 13 }; GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 11 }; val.normal.textColor = new Color(0.72f, 0.72f, 0.72f); _dim = val; } } } [BepInPlugin("opencode.craftplanner", "Craft Planner", "0.1.0")] public class CraftPlannerPlugin : BaseUnityPlugin { public const string Guid = "opencode.craftplanner"; public const string Version = "0.1.0"; internal static CraftPlannerPlugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<KeyCode> ToggleKey; internal static ConfigEntry<float> ScanRadius; internal static ConfigEntry<bool> DecomposeProcessing; internal static ConfigEntry<string> UiLanguage; private PlannerUI _ui; private Harmony _harmony; private void Awake() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)287, "Key that opens/closes the craft planner window."); ScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanRadius", 40f, "Radius in meters used when counting materials in nearby containers."); DecomposeProcessing = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DecomposeProcessing", true, "Show smelted bars and milled goods as their source material (ore, barley)."); UiLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "UiLanguage", "auto", new ConfigDescription("UI language: auto (follow the game), en or ru.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "auto", "en", "ru" }), Array.Empty<object>())); Strings.Refresh(); _ui = new PlannerUI(); _harmony = new Harmony("opencode.craftplanner"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Craft Planner v0.1.0 loaded. Toggle with " + ((object)ToggleKey.Value/*cast due to .constrained prefix*/).ToString())); } private void Update() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_ui != null) { if (Input.GetKeyDown(ToggleKey.Value) && !Menu.IsVisible() && !TextInput.IsVisible()) { _ui.Toggle(); } if (_ui.IsOpen) { _ui.Tick(); } } } private void OnGUI() { if (_ui != null) { if (!_ui.IsOpen) { _ui.Prewarm(); } _ui.Draw(); } } private void OnDestroy() { _ui?.Close(); _ui = null; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } public static class StorageScanner { public static Dictionary<string, int> Scan(float radius) { //IL_0035: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, int> result = new Dictionary<string, int>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return result; } Accumulate(((Humanoid)localPlayer).GetInventory(), result); Vector3 position = ((Component)localPlayer).transform.position; float num = radius * radius; Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); Container[] array2 = array; foreach (Container val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { Accumulate(val.GetInventory(), result); } } } return result; } private static void Accumulate(Inventory inventory, Dictionary<string, int> result) { if (inventory == null) { return; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_shared != null) { string name = allItem.m_shared.m_name; if (!string.IsNullOrEmpty(name)) { result.TryGetValue(name, out var value); result[name] = value + allItem.m_stack; } } } } } internal static class Strings { private static readonly Dictionary<string, string> En = new Dictionary<string, string> { { "Title", "Craft Planner" }, { "Search", "Search (ru/en)" }, { "NothingFound", "Nothing found." }, { "LoadWorld", "Load a world to use the planner." }, { "Pick", "Pick an item or a building piece on the left." }, { "Target", "Target:" }, { "Quality", "Quality:" }, { "Ingredients", "Ingredients" }, { "Raw", "Needed raw materials" }, { "Rescan", "Rescan containers" }, { "Close", "F6 to close" }, { "Entries", "entries" }, { "Station", "Station:" }, { "Output", "Output: x{0} per craft" }, { "Have", "have" }, { "NoMaterials", "No materials needed." }, { "Build", "build" }, { "Craft", "craft" }, { "Smelted", "smelted" } }; private static readonly Dictionary<string, string> Ru = new Dictionary<string, string> { { "Title", "Планировщик крафта" }, { "Search", "Поиск (рус/англ)" }, { "NothingFound", "Ничего не найдено." }, { "LoadWorld", "Загрузи мир, чтобы пользоваться планировщиком." }, { "Pick", "Выбери предмет или постройку слева." }, { "Target", "Нужно:" }, { "Quality", "Качество:" }, { "Ingredients", "Состав" }, { "Raw", "Нужно сырья" }, { "Rescan", "Обновить сундуки" }, { "Close", "F6 — закрыть" }, { "Entries", "позиций" }, { "Station", "Станция:" }, { "Output", "Выход: x{0} за крафт" }, { "Have", "есть" }, { "NoMaterials", "Материалы не нужны." }, { "Build", "постройка" }, { "Craft", "крафт" }, { "Smelted", "плавка" } }; public static bool IsRussian { get; private set; } public static void Refresh() { string a = ((CraftPlannerPlugin.UiLanguage != null) ? CraftPlannerPlugin.UiLanguage.Value : "auto"); if (string.Equals(a, "ru", StringComparison.OrdinalIgnoreCase)) { IsRussian = true; return; } if (string.Equals(a, "en", StringComparison.OrdinalIgnoreCase)) { IsRussian = false; return; } bool isRussian = false; try { Localization instance = Localization.instance; string text = ((instance != null) ? instance.GetSelectedLanguage() : null); isRussian = !string.IsNullOrEmpty(text) && text.IndexOf("Russian", StringComparison.OrdinalIgnoreCase) >= 0; } catch { } IsRussian = isRussian; } public static string T(string key) { Dictionary<string, string> dictionary = (IsRussian ? Ru : En); string value; return dictionary.TryGetValue(key, out value) ? value : key; } public static string Format(string key, object arg) { return string.Format(T(key), arg); } }
dist/CraftPlanner.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.0.0.0")] namespace CraftPlanner; internal static class CameraPatches { [HarmonyPatch(typeof(GameCamera), "LateUpdate")] private static class GameCameraLateUpdatePatch { private static bool Prefix() { return !PlannerUI.Visible; } } [HarmonyPatch(typeof(InventoryGui), "IsVisible")] private static class InventoryGuiVisiblePatch { private static void Postfix(ref bool __result) { if (PlannerUI.Visible) { __result = true; } } } } public sealed class RecipeEntry { public string Key; public string Display; public string SearchIndex = string.Empty; public string Station; public int StationLevel; public int OutputAmount = 1; public bool IsPiece; public Recipe Recipe; public Piece Piece; public Requirement[] Resources => IsPiece ? Piece.m_resources : Recipe.m_resources; } public sealed class GameData { private static GameData _cached; private static ObjectDB _cachedDb; public readonly Dictionary<string, RecipeEntry> ByKey = new Dictionary<string, RecipeEntry>(); public readonly Dictionary<string, string> DisplayNames = new Dictionary<string, string>(); public readonly List<RecipeEntry> All = new List<RecipeEntry>(); private Dictionary<string, string> _processing; private static FieldRef<ObjectDB, List<Piece>> _buildPiecesRef; private static bool _buildPiecesResolved; public Dictionary<string, string> Processing { get { if (_processing == null) { BuildProcessing(); } return _processing; } } public static GameData Get() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } if (_cached != null && (Object)(object)_cachedDb == (Object)(object)instance) { return _cached; } _cachedDb = instance; _cached = Build(instance); return _cached; } public string DisplayOf(string key) { if (string.IsNullOrEmpty(key)) { return "?"; } if (DisplayNames.TryGetValue(key, out var value)) { return value; } return Loc(key); } public static string Loc(string s) { if (string.IsNullOrEmpty(s)) { return s; } try { Localization instance = Localization.instance; if (instance != null) { return instance.Localize(s); } } catch { } return s; } private static GameData Build(ObjectDB odb) { GameData gameData = new GameData(); if (odb.m_items != null) { foreach (GameObject item2 in odb.m_items) { if ((Object)(object)item2 == (Object)null) { continue; } ItemDrop component = item2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { string name = component.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name) && !gameData.DisplayNames.ContainsKey(name)) { gameData.DisplayNames[name] = Loc(name); } } } } if (odb.m_recipes != null) { foreach (Recipe recipe in odb.m_recipes) { if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || recipe.m_resources == null || recipe.m_resources.Length == 0) { continue; } ItemDrop item = recipe.m_item; if ((Object)(object)item == (Object)null || item.m_itemData == null || item.m_itemData.m_shared == null) { continue; } string name2 = item.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name2)) { RecipeEntry recipeEntry = new RecipeEntry { Key = name2, Display = gameData.DisplayOf(name2), Station = (((Object)(object)recipe.m_craftingStation != (Object)null) ? Loc(recipe.m_craftingStation.m_name) : null), StationLevel = recipe.m_minStationLevel, OutputAmount = Mathf.Max(1, recipe.m_amount), IsPiece = false, Recipe = recipe }; recipeEntry.SearchIndex = BuildSearchIndex(gameData, name2, recipeEntry.Station); gameData.All.Add(recipeEntry); if (!gameData.ByKey.ContainsKey(name2)) { gameData.ByKey[name2] = recipeEntry; } } } } List<Piece> buildPieces = GetBuildPieces(odb); if (buildPieces != null) { foreach (Piece item3 in buildPieces) { if (!((Object)(object)item3 == (Object)null) && item3.m_resources != null && item3.m_resources.Length != 0 && !string.IsNullOrEmpty(item3.m_name)) { string name3 = item3.m_name; if (!gameData.ByKey.ContainsKey(name3)) { RecipeEntry recipeEntry2 = new RecipeEntry { Key = name3, Display = gameData.DisplayOf(name3), Station = null, StationLevel = 0, OutputAmount = 1, IsPiece = true, Piece = item3 }; recipeEntry2.SearchIndex = BuildSearchIndex(gameData, name3, null); gameData.All.Add(recipeEntry2); gameData.ByKey[name3] = recipeEntry2; } } } } gameData.All.Sort((RecipeEntry a, RecipeEntry b) => string.Compare(a.Display, b.Display, StringComparison.CurrentCultureIgnoreCase)); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Indexed " + gameData.All.Count + " recipes/pieces")); } return gameData; } private static string BuildSearchIndex(GameData data, string key, string station) { StringBuilder stringBuilder = new StringBuilder(); string value = data.DisplayOf(key); if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(value); } stringBuilder.Append(' ').Append(key.TrimStart('$').Replace('_', ' ')); if (!string.IsNullOrEmpty(station)) { stringBuilder.Append(' ').Append(station); } return stringBuilder.ToString().ToLowerInvariant(); } private static List<Piece> GetBuildPieces(ObjectDB odb) { if (!_buildPiecesResolved) { _buildPiecesResolved = true; try { _buildPiecesRef = AccessTools.FieldRefAccess<ObjectDB, List<Piece>>("m_buildPieces"); } catch (Exception) { _buildPiecesRef = null; } } if (_buildPiecesRef != null) { try { return _buildPiecesRef.Invoke(odb); } catch (Exception) { } } try { List<Piece> list = new List<Piece>(); Piece[] array = Resources.FindObjectsOfTypeAll<Piece>(); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null) && val.m_resources != null && val.m_resources.Length != 0 && !string.IsNullOrEmpty(val.m_name)) { list.Add(val); } } return list; } catch (Exception) { return null; } } private void BuildProcessing() { _processing = new Dictionary<string, string>(); ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Smelter component = prefab.GetComponent<Smelter>(); if ((Object)(object)component == (Object)null || component.m_conversion == null) { continue; } foreach (ItemConversion item in component.m_conversion) { if (item == null || (Object)(object)item.m_from == (Object)null || (Object)(object)item.m_to == (Object)null || item.m_from.m_itemData == null || item.m_from.m_itemData.m_shared == null || item.m_to.m_itemData == null || item.m_to.m_itemData.m_shared == null) { continue; } string name = item.m_from.m_itemData.m_shared.m_name; string name2 = item.m_to.m_itemData.m_shared.m_name; if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(name2) || name == name2) { continue; } if (_processing.TryGetValue(name2, out var value)) { if (value != name) { hashSet.Add(name2); } } else { _processing[name2] = name; } } } foreach (string item2 in hashSet) { _processing.Remove(item2); } } } public sealed class MatNode { public string Key; public string Display; public int Amount; public string Note; public readonly List<MatNode> Children = new List<MatNode>(); public bool IsRaw => Children.Count == 0; } public static class Materials { private const int MaxDepth = 16; public static MatNode Build(string key, int amount, int quality) { GameData gameData = GameData.Get(); if (gameData == null) { return null; } HashSet<string> path = new HashSet<string>(); return Expand(key, amount, quality, gameData, path, 0); } public static Dictionary<string, int> Totals(MatNode root) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (root == null) { return dictionary; } Collect(root, dictionary); return dictionary; } private static void Collect(MatNode node, Dictionary<string, int> totals) { if (node.IsRaw) { totals.TryGetValue(node.Key, out var value); totals[node.Key] = value + node.Amount; return; } foreach (MatNode child in node.Children) { Collect(child, totals); } } private static MatNode Expand(string key, int amount, int quality, GameData data, HashSet<string> path, int depth) { MatNode matNode = new MatNode { Key = key, Display = data.DisplayOf(key), Amount = amount }; if (amount <= 0) { return matNode; } if (depth >= 16 || path.Contains(key)) { return matNode; } if (data.ByKey.TryGetValue(key, out var value)) { Requirement[] resources = value.Resources; if (resources == null || resources.Length == 0) { return matNode; } int num = Math.Max(1, value.OutputAmount); int num2 = (amount + num - 1) / num; matNode.Note = (value.IsPiece ? "build" : ((value.Station != null) ? (value.Station + ((value.StationLevel > 1) ? (" lvl " + value.StationLevel) : "")) : "craft")); path.Add(key); Requirement[] array = resources; foreach (Requirement val in array) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null && val.m_resItem.m_itemData.m_shared != null) { int amount2 = val.GetAmount(quality); if (amount2 > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; MatNode item = Expand(name, amount2 * num2, quality, data, path, depth + 1); matNode.Children.Add(item); } } } path.Remove(key); return matNode; } if (CraftPlannerPlugin.DecomposeProcessing != null && CraftPlannerPlugin.DecomposeProcessing.Value && data.Processing.TryGetValue(key, out var value2)) { path.Add(key); MatNode matNode2 = Expand(value2, amount, quality, data, path, depth + 1); path.Remove(key); matNode2.Note = "smelted"; matNode.Children.Add(matNode2); } return matNode; } } internal sealed class PlannerUI { private const int WindowId = 202047470; private static readonly Color ColorOk = new Color(0.62f, 0.95f, 0.62f); private static readonly Color ColorMissing = new Color(1f, 0.78f, 0.42f); private static readonly Color ColorRaw = new Color(0.8f, 0.88f, 1f); private static readonly Color ColorNote = new Color(0.62f, 0.62f, 0.62f); private Rect _rect = new Rect(90f, 70f, 880f, 600f); private string _search = string.Empty; private List<RecipeEntry> _filtered = new List<RecipeEntry>(); private RecipeEntry _selected; private int _quality = 1; private int _maxQuality = 1; private int _target = 1; private Vector2 _listScroll; private Vector2 _detailScroll; private GameData _data; private MatNode _tree; private Dictionary<string, int> _totals = new Dictionary<string, int>(); private Dictionary<string, int> _have = new Dictionary<string, int>(); private bool _prevCursorVisible; private CursorLockMode _prevCursorLock; private bool _errorLogged; private GUIStyle _title; private GUIStyle _section; private GUIStyle _node; private GUIStyle _dim; public bool IsOpen { get; private set; } public static bool Visible { get; private set; } public void Toggle() { if (IsOpen) { Close(); } else { Open(); } } public void Open() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { Strings.Refresh(); _prevCursorVisible = Cursor.visible; _prevCursorLock = Cursor.lockState; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; IsOpen = true; Visible = true; Stopwatch stopwatch = Stopwatch.StartNew(); RefreshData(); Rescan(); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Planner opened in " + stopwatch.ElapsedMilliseconds + " ms")); } } } public void Close() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { IsOpen = false; Visible = false; Cursor.visible = _prevCursorVisible; Cursor.lockState = _prevCursorLock; } } public void Tick() { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } private void RefreshData() { try { _data = GameData.Get(); } catch (Exception ex) { CraftPlannerPlugin.Log.LogError((object)("Failed to index game data: " + ex)); _data = null; } ApplyFilter(); if (_selected != null) { _selected = ((_data != null && _data.ByKey.TryGetValue(_selected.Key, out var value)) ? value : null); } Rebuild(); } private void ApplyFilter() { _filtered.Clear(); if (_data == null) { return; } string text = (_search ?? string.Empty).Trim(); if (text.Length == 0) { _filtered.AddRange(_data.All); return; } foreach (RecipeEntry item in _data.All) { if (!string.IsNullOrEmpty(item.SearchIndex) && item.SearchIndex.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { _filtered.Add(item); } } } private void Select(RecipeEntry entry) { //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) _selected = entry; _quality = 1; _maxQuality = MaxQuality(entry); _detailScroll = Vector2.zero; Rebuild(); } private static int MaxQuality(RecipeEntry entry) { if (entry == null || entry.IsPiece) { return 1; } if ((Object)(object)entry.Recipe == (Object)null || (Object)(object)entry.Recipe.m_item == (Object)null) { return 1; } SharedData val = ((entry.Recipe.m_item.m_itemData != null) ? entry.Recipe.m_item.m_itemData.m_shared : null); return (val == null) ? 1 : Mathf.Max(1, val.m_maxQuality); } private void Rebuild() { if (_selected == null || _data == null) { _tree = null; _totals = new Dictionary<string, int>(); } else { _tree = Materials.Build(_selected.Key, Mathf.Max(1, _target), _quality); _totals = Materials.Totals(_tree); } } private void Rescan() { if ((Object)(object)Player.m_localPlayer == (Object)null) { _have = new Dictionary<string, int>(); } else { _have = StorageScanner.Scan(Mathf.Max(1f, CraftPlannerPlugin.ScanRadius.Value)); } } public void Draw() { //IL_0023: 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_0052: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { return; } try { InitStyles(); _rect = GUILayout.Window(202047470, _rect, new WindowFunction(DrawWindow), Strings.T("Title") + " v0.1.0", Array.Empty<GUILayoutOption>()); } catch (Exception ex) { if (!_errorLogged) { _errorLogged = true; CraftPlannerPlugin.Log.LogError((object)("Planner UI error: " + ex)); } Close(); } } public void Prewarm() { try { InitStyles(); } catch { } } private void DrawWindow(int id) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (_data == null && (Object)(object)Player.m_localPlayer != (Object)null) { RefreshData(); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DrawList(); GUILayout.Space(8f); DrawDetails(); GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private void DrawList() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_011f: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label(Strings.T("Search"), _dim, Array.Empty<GUILayoutOption>()); string text = GUILayout.TextField(_search ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }); if (text != _search) { _search = text; ApplyFilter(); } GUILayout.Space(6f); _listScroll = GUILayout.BeginScrollView(_listScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_data == null) { GUILayout.Label(Strings.T("LoadWorld"), _node, Array.Empty<GUILayoutOption>()); } else { foreach (RecipeEntry item in _filtered) { bool flag = _selected != null && _selected.Key == item.Key; Color backgroundColor = GUI.backgroundColor; if (flag) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(item.Display, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { Select(item); } GUI.backgroundColor = backgroundColor; } if (_filtered.Count == 0) { GUILayout.Label(Strings.T("NothingFound"), _dim, Array.Empty<GUILayoutOption>()); } } GUILayout.EndScrollView(); GUILayout.Label(Strings.T("Close") + " • " + _filtered.Count + " " + Strings.T("Entries"), _dim, Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); } private void DrawDetails() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //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_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); _detailScroll = GUILayout.BeginScrollView(_detailScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_selected == null) { GUILayout.Label(Strings.T("Pick"), _node, Array.Empty<GUILayoutOption>()); } else { GUILayout.Label(_selected.Display, _title, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(_selected.Station)) { GUILayout.Label(Strings.T("Station") + " " + _selected.Station + ((_selected.StationLevel > 1) ? (" lvl " + _selected.StationLevel) : string.Empty), _dim, Array.Empty<GUILayoutOption>()); } if (!_selected.IsPiece && _selected.OutputAmount > 1) { GUILayout.Label(Strings.Format("Output", _selected.OutputAmount), _dim, Array.Empty<GUILayoutOption>()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Target"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _target = AmountButton(1, _target); _target = AmountButton(5, _target); _target = AmountButton(10, _target); _target = AmountButton(25, _target); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (_maxQuality > 1) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Quality"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); for (int i = 1; i <= _maxQuality; i++) { Color backgroundColor = GUI.backgroundColor; if (i == _quality) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(i.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) }) && i != _quality) { _quality = i; Rebuild(); } GUI.backgroundColor = backgroundColor; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(6f); GUILayout.Label(Strings.T("Ingredients"), _section, Array.Empty<GUILayoutOption>()); if (_tree != null) { DrawNode(_tree, 0); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Raw"), _section, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button(Strings.T("Rescan"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) })) { Rescan(); } GUILayout.EndHorizontal(); DrawTotals(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private int AmountButton(int value, int current) { //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_001e: 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) Color backgroundColor = GUI.backgroundColor; if (current == value) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } int num = (GUILayout.Button("×" + value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) }) ? value : current); GUI.backgroundColor = backgroundColor; if (num != current) { Rebuild(); } return num; } private void DrawNode(MatNode node, int depth) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Space((float)depth * 18f); if (node.IsRaw) { GUI.color = ColorRaw; } else { GUI.color = Color.white; } GUILayout.Label(node.Display + " ×" + node.Amount, _node, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(node.Note)) { GUI.color = ColorNote; GUILayout.Label("[" + NoteName(node.Note) + "]", _dim, Array.Empty<GUILayoutOption>()); } GUI.color = Color.white; if (node.IsRaw) { _have.TryGetValue(node.Key, out var value); GUI.color = ((value >= node.Amount) ? ColorOk : ColorMissing); GUILayout.Label(Strings.T("Have") + " " + value, _dim, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); foreach (MatNode child in node.Children) { DrawNode(child, depth + 1); } } private static string NoteName(string note) { return note switch { "build" => Strings.T("Build"), "craft" => Strings.T("Craft"), "smelted" => Strings.T("Smelted"), _ => note, }; } private void DrawTotals() { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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) if (_totals == null || _totals.Count == 0) { GUILayout.Label(Strings.T("NoMaterials"), _dim, Array.Empty<GUILayoutOption>()); return; } List<string> list = new List<string>(_totals.Keys); list.Sort((string a, string b) => string.Compare(_data.DisplayOf(a), _data.DisplayOf(b), StringComparison.CurrentCultureIgnoreCase)); foreach (string item in list) { int num = _totals[item]; _have.TryGetValue(item, out var value); GUI.color = ((value >= num) ? ColorOk : ColorMissing); GUILayout.Label(_data.DisplayOf(item) + " " + value + " / " + num, _node, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } } private void InitStyles() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0044: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //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) //IL_007d: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown if (_title == null) { _title = new GUIStyle(GUI.skin.label) { fontSize = 19, fontStyle = (FontStyle)1 }; _section = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)1 }; _node = new GUIStyle(GUI.skin.label) { fontSize = 13 }; GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 11 }; val.normal.textColor = new Color(0.72f, 0.72f, 0.72f); _dim = val; } } } [BepInPlugin("opencode.craftplanner", "Craft Planner", "0.1.0")] public class CraftPlannerPlugin : BaseUnityPlugin { public const string Guid = "opencode.craftplanner"; public const string Version = "0.1.0"; internal static CraftPlannerPlugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<KeyCode> ToggleKey; internal static ConfigEntry<float> ScanRadius; internal static ConfigEntry<bool> DecomposeProcessing; internal static ConfigEntry<string> UiLanguage; private PlannerUI _ui; private Harmony _harmony; private void Awake() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)287, "Key that opens/closes the craft planner window."); ScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanRadius", 40f, "Radius in meters used when counting materials in nearby containers."); DecomposeProcessing = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DecomposeProcessing", true, "Show smelted bars and milled goods as their source material (ore, barley)."); UiLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "UiLanguage", "auto", new ConfigDescription("UI language: auto (follow the game), en or ru.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "auto", "en", "ru" }), Array.Empty<object>())); Strings.Refresh(); _ui = new PlannerUI(); _harmony = new Harmony("opencode.craftplanner"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Craft Planner v0.1.0 loaded. Toggle with " + ((object)ToggleKey.Value/*cast due to .constrained prefix*/).ToString())); } private void Update() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_ui != null) { if (Input.GetKeyDown(ToggleKey.Value) && !Menu.IsVisible() && !TextInput.IsVisible()) { _ui.Toggle(); } if (_ui.IsOpen) { _ui.Tick(); } } } private void OnGUI() { if (_ui != null) { if (!_ui.IsOpen) { _ui.Prewarm(); } _ui.Draw(); } } private void OnDestroy() { _ui?.Close(); _ui = null; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } public static class StorageScanner { public static Dictionary<string, int> Scan(float radius) { //IL_0035: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, int> result = new Dictionary<string, int>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return result; } Accumulate(((Humanoid)localPlayer).GetInventory(), result); Vector3 position = ((Component)localPlayer).transform.position; float num = radius * radius; Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); Container[] array2 = array; foreach (Container val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { Accumulate(val.GetInventory(), result); } } } return result; } private static void Accumulate(Inventory inventory, Dictionary<string, int> result) { if (inventory == null) { return; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_shared != null) { string name = allItem.m_shared.m_name; if (!string.IsNullOrEmpty(name)) { result.TryGetValue(name, out var value); result[name] = value + allItem.m_stack; } } } } } internal static class Strings { private static readonly Dictionary<string, string> En = new Dictionary<string, string> { { "Title", "Craft Planner" }, { "Search", "Search (ru/en)" }, { "NothingFound", "Nothing found." }, { "LoadWorld", "Load a world to use the planner." }, { "Pick", "Pick an item or a building piece on the left." }, { "Target", "Target:" }, { "Quality", "Quality:" }, { "Ingredients", "Ingredients" }, { "Raw", "Needed raw materials" }, { "Rescan", "Rescan containers" }, { "Close", "F6 to close" }, { "Entries", "entries" }, { "Station", "Station:" }, { "Output", "Output: x{0} per craft" }, { "Have", "have" }, { "NoMaterials", "No materials needed." }, { "Build", "build" }, { "Craft", "craft" }, { "Smelted", "smelted" } }; private static readonly Dictionary<string, string> Ru = new Dictionary<string, string> { { "Title", "Планировщик крафта" }, { "Search", "Поиск (рус/англ)" }, { "NothingFound", "Ничего не найдено." }, { "LoadWorld", "Загрузи мир, чтобы пользоваться планировщиком." }, { "Pick", "Выбери предмет или постройку слева." }, { "Target", "Нужно:" }, { "Quality", "Качество:" }, { "Ingredients", "Состав" }, { "Raw", "Нужно сырья" }, { "Rescan", "Обновить сундуки" }, { "Close", "F6 — закрыть" }, { "Entries", "позиций" }, { "Station", "Станция:" }, { "Output", "Выход: x{0} за крафт" }, { "Have", "есть" }, { "NoMaterials", "Материалы не нужны." }, { "Build", "постройка" }, { "Craft", "крафт" }, { "Smelted", "плавка" } }; public static bool IsRussian { get; private set; } public static void Refresh() { string a = ((CraftPlannerPlugin.UiLanguage != null) ? CraftPlannerPlugin.UiLanguage.Value : "auto"); if (string.Equals(a, "ru", StringComparison.OrdinalIgnoreCase)) { IsRussian = true; return; } if (string.Equals(a, "en", StringComparison.OrdinalIgnoreCase)) { IsRussian = false; return; } bool isRussian = false; try { Localization instance = Localization.instance; string text = ((instance != null) ? instance.GetSelectedLanguage() : null); isRussian = !string.IsNullOrEmpty(text) && text.IndexOf("Russian", StringComparison.OrdinalIgnoreCase) >= 0; } catch { } IsRussian = isRussian; } public static string T(string key) { Dictionary<string, string> dictionary = (IsRussian ? Ru : En); string value; return dictionary.TryGetValue(key, out value) ? value : key; } public static string Format(string key, object arg) { return string.Format(T(key), arg); } }
obj/Debug/CraftPlanner.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyVersion("0.0.0.0")] namespace CraftPlanner; internal static class CameraPatches { [HarmonyPatch(typeof(GameCamera), "LateUpdate")] private static class GameCameraLateUpdatePatch { private static bool Prefix() { return !PlannerUI.Visible; } } [HarmonyPatch(typeof(InventoryGui), "IsVisible")] private static class InventoryGuiVisiblePatch { private static void Postfix(ref bool __result) { if (PlannerUI.Visible) { __result = true; } } } } public sealed class RecipeEntry { public string Key; public string Display; public string SearchIndex = string.Empty; public string Station; public int StationLevel; public int OutputAmount = 1; public bool IsPiece; public Recipe Recipe; public Piece Piece; public Requirement[] Resources => IsPiece ? Piece.m_resources : Recipe.m_resources; } public sealed class GameData { private static GameData _cached; private static ObjectDB _cachedDb; public readonly Dictionary<string, RecipeEntry> ByKey = new Dictionary<string, RecipeEntry>(); public readonly Dictionary<string, string> DisplayNames = new Dictionary<string, string>(); public readonly List<RecipeEntry> All = new List<RecipeEntry>(); private Dictionary<string, string> _processing; private static FieldRef<ObjectDB, List<Piece>> _buildPiecesRef; private static bool _buildPiecesResolved; public Dictionary<string, string> Processing { get { if (_processing == null) { BuildProcessing(); } return _processing; } } public static GameData Get() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } if (_cached != null && (Object)(object)_cachedDb == (Object)(object)instance) { return _cached; } _cachedDb = instance; _cached = Build(instance); return _cached; } public string DisplayOf(string key) { if (string.IsNullOrEmpty(key)) { return "?"; } if (DisplayNames.TryGetValue(key, out var value)) { return value; } return Loc(key); } public static string Loc(string s) { if (string.IsNullOrEmpty(s)) { return s; } try { Localization instance = Localization.instance; if (instance != null) { return instance.Localize(s); } } catch { } return s; } private static GameData Build(ObjectDB odb) { GameData gameData = new GameData(); if (odb.m_items != null) { foreach (GameObject item2 in odb.m_items) { if ((Object)(object)item2 == (Object)null) { continue; } ItemDrop component = item2.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { string name = component.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name) && !gameData.DisplayNames.ContainsKey(name)) { gameData.DisplayNames[name] = Loc(name); } } } } if (odb.m_recipes != null) { foreach (Recipe recipe in odb.m_recipes) { if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || recipe.m_resources == null || recipe.m_resources.Length == 0) { continue; } ItemDrop item = recipe.m_item; if ((Object)(object)item == (Object)null || item.m_itemData == null || item.m_itemData.m_shared == null) { continue; } string name2 = item.m_itemData.m_shared.m_name; if (!string.IsNullOrEmpty(name2)) { RecipeEntry recipeEntry = new RecipeEntry { Key = name2, Display = gameData.DisplayOf(name2), Station = (((Object)(object)recipe.m_craftingStation != (Object)null) ? Loc(recipe.m_craftingStation.m_name) : null), StationLevel = recipe.m_minStationLevel, OutputAmount = Mathf.Max(1, recipe.m_amount), IsPiece = false, Recipe = recipe }; recipeEntry.SearchIndex = BuildSearchIndex(gameData, name2, recipeEntry.Station); gameData.All.Add(recipeEntry); if (!gameData.ByKey.ContainsKey(name2)) { gameData.ByKey[name2] = recipeEntry; } } } } List<Piece> buildPieces = GetBuildPieces(odb); if (buildPieces != null) { foreach (Piece item3 in buildPieces) { if (!((Object)(object)item3 == (Object)null) && item3.m_resources != null && item3.m_resources.Length != 0 && !string.IsNullOrEmpty(item3.m_name)) { string name3 = item3.m_name; if (!gameData.ByKey.ContainsKey(name3)) { RecipeEntry recipeEntry2 = new RecipeEntry { Key = name3, Display = gameData.DisplayOf(name3), Station = null, StationLevel = 0, OutputAmount = 1, IsPiece = true, Piece = item3 }; recipeEntry2.SearchIndex = BuildSearchIndex(gameData, name3, null); gameData.All.Add(recipeEntry2); gameData.ByKey[name3] = recipeEntry2; } } } } gameData.All.Sort((RecipeEntry a, RecipeEntry b) => string.Compare(a.Display, b.Display, StringComparison.CurrentCultureIgnoreCase)); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Indexed " + gameData.All.Count + " recipes/pieces")); } return gameData; } private static string BuildSearchIndex(GameData data, string key, string station) { StringBuilder stringBuilder = new StringBuilder(); string value = data.DisplayOf(key); if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(value); } stringBuilder.Append(' ').Append(key.TrimStart('$').Replace('_', ' ')); if (!string.IsNullOrEmpty(station)) { stringBuilder.Append(' ').Append(station); } return stringBuilder.ToString().ToLowerInvariant(); } private static List<Piece> GetBuildPieces(ObjectDB odb) { if (!_buildPiecesResolved) { _buildPiecesResolved = true; try { _buildPiecesRef = AccessTools.FieldRefAccess<ObjectDB, List<Piece>>("m_buildPieces"); } catch (Exception) { _buildPiecesRef = null; } } if (_buildPiecesRef != null) { try { return _buildPiecesRef.Invoke(odb); } catch (Exception) { } } try { List<Piece> list = new List<Piece>(); Piece[] array = Resources.FindObjectsOfTypeAll<Piece>(); foreach (Piece val in array) { if (!((Object)(object)val == (Object)null) && val.m_resources != null && val.m_resources.Length != 0 && !string.IsNullOrEmpty(val.m_name)) { list.Add(val); } } return list; } catch (Exception) { return null; } } private void BuildProcessing() { _processing = new Dictionary<string, string>(); ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return; } HashSet<string> hashSet = new HashSet<string>(); foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Smelter component = prefab.GetComponent<Smelter>(); if ((Object)(object)component == (Object)null || component.m_conversion == null) { continue; } foreach (ItemConversion item in component.m_conversion) { if (item == null || (Object)(object)item.m_from == (Object)null || (Object)(object)item.m_to == (Object)null || item.m_from.m_itemData == null || item.m_from.m_itemData.m_shared == null || item.m_to.m_itemData == null || item.m_to.m_itemData.m_shared == null) { continue; } string name = item.m_from.m_itemData.m_shared.m_name; string name2 = item.m_to.m_itemData.m_shared.m_name; if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(name2) || name == name2) { continue; } if (_processing.TryGetValue(name2, out var value)) { if (value != name) { hashSet.Add(name2); } } else { _processing[name2] = name; } } } foreach (string item2 in hashSet) { _processing.Remove(item2); } } } public sealed class MatNode { public string Key; public string Display; public int Amount; public string Note; public readonly List<MatNode> Children = new List<MatNode>(); public bool IsRaw => Children.Count == 0; } public static class Materials { private const int MaxDepth = 16; public static MatNode Build(string key, int amount, int quality) { GameData gameData = GameData.Get(); if (gameData == null) { return null; } HashSet<string> path = new HashSet<string>(); return Expand(key, amount, quality, gameData, path, 0); } public static Dictionary<string, int> Totals(MatNode root) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (root == null) { return dictionary; } Collect(root, dictionary); return dictionary; } private static void Collect(MatNode node, Dictionary<string, int> totals) { if (node.IsRaw) { totals.TryGetValue(node.Key, out var value); totals[node.Key] = value + node.Amount; return; } foreach (MatNode child in node.Children) { Collect(child, totals); } } private static MatNode Expand(string key, int amount, int quality, GameData data, HashSet<string> path, int depth) { MatNode matNode = new MatNode { Key = key, Display = data.DisplayOf(key), Amount = amount }; if (amount <= 0) { return matNode; } if (depth >= 16 || path.Contains(key)) { return matNode; } if (data.ByKey.TryGetValue(key, out var value)) { Requirement[] resources = value.Resources; if (resources == null || resources.Length == 0) { return matNode; } int num = Math.Max(1, value.OutputAmount); int num2 = (amount + num - 1) / num; matNode.Note = (value.IsPiece ? "build" : ((value.Station != null) ? (value.Station + ((value.StationLevel > 1) ? (" lvl " + value.StationLevel) : "")) : "craft")); path.Add(key); Requirement[] array = resources; foreach (Requirement val in array) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null && val.m_resItem.m_itemData.m_shared != null) { int amount2 = val.GetAmount(quality); if (amount2 > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; MatNode item = Expand(name, amount2 * num2, quality, data, path, depth + 1); matNode.Children.Add(item); } } } path.Remove(key); return matNode; } if (CraftPlannerPlugin.DecomposeProcessing != null && CraftPlannerPlugin.DecomposeProcessing.Value && data.Processing.TryGetValue(key, out var value2)) { path.Add(key); MatNode matNode2 = Expand(value2, amount, quality, data, path, depth + 1); path.Remove(key); matNode2.Note = "smelted"; matNode.Children.Add(matNode2); } return matNode; } } internal sealed class PlannerUI { private const int WindowId = 202047470; private static readonly Color ColorOk = new Color(0.62f, 0.95f, 0.62f); private static readonly Color ColorMissing = new Color(1f, 0.78f, 0.42f); private static readonly Color ColorRaw = new Color(0.8f, 0.88f, 1f); private static readonly Color ColorNote = new Color(0.62f, 0.62f, 0.62f); private Rect _rect = new Rect(90f, 70f, 880f, 600f); private string _search = string.Empty; private List<RecipeEntry> _filtered = new List<RecipeEntry>(); private RecipeEntry _selected; private int _quality = 1; private int _maxQuality = 1; private int _target = 1; private Vector2 _listScroll; private Vector2 _detailScroll; private GameData _data; private MatNode _tree; private Dictionary<string, int> _totals = new Dictionary<string, int>(); private Dictionary<string, int> _have = new Dictionary<string, int>(); private bool _prevCursorVisible; private CursorLockMode _prevCursorLock; private bool _errorLogged; private GUIStyle _title; private GUIStyle _section; private GUIStyle _node; private GUIStyle _dim; public bool IsOpen { get; private set; } public static bool Visible { get; private set; } public void Toggle() { if (IsOpen) { Close(); } else { Open(); } } public void Open() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { Strings.Refresh(); _prevCursorVisible = Cursor.visible; _prevCursorLock = Cursor.lockState; Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; IsOpen = true; Visible = true; Stopwatch stopwatch = Stopwatch.StartNew(); RefreshData(); Rescan(); if (CraftPlannerPlugin.Log != null) { CraftPlannerPlugin.Log.LogInfo((object)("Planner opened in " + stopwatch.ElapsedMilliseconds + " ms")); } } } public void Close() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { IsOpen = false; Visible = false; Cursor.visible = _prevCursorVisible; Cursor.lockState = _prevCursorLock; } } public void Tick() { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } private void RefreshData() { try { _data = GameData.Get(); } catch (Exception ex) { CraftPlannerPlugin.Log.LogError((object)("Failed to index game data: " + ex)); _data = null; } ApplyFilter(); if (_selected != null) { _selected = ((_data != null && _data.ByKey.TryGetValue(_selected.Key, out var value)) ? value : null); } Rebuild(); } private void ApplyFilter() { _filtered.Clear(); if (_data == null) { return; } string text = (_search ?? string.Empty).Trim(); if (text.Length == 0) { _filtered.AddRange(_data.All); return; } foreach (RecipeEntry item in _data.All) { if (!string.IsNullOrEmpty(item.SearchIndex) && item.SearchIndex.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { _filtered.Add(item); } } } private void Select(RecipeEntry entry) { //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) _selected = entry; _quality = 1; _maxQuality = MaxQuality(entry); _detailScroll = Vector2.zero; Rebuild(); } private static int MaxQuality(RecipeEntry entry) { if (entry == null || entry.IsPiece) { return 1; } if ((Object)(object)entry.Recipe == (Object)null || (Object)(object)entry.Recipe.m_item == (Object)null) { return 1; } SharedData val = ((entry.Recipe.m_item.m_itemData != null) ? entry.Recipe.m_item.m_itemData.m_shared : null); return (val == null) ? 1 : Mathf.Max(1, val.m_maxQuality); } private void Rebuild() { if (_selected == null || _data == null) { _tree = null; _totals = new Dictionary<string, int>(); } else { _tree = Materials.Build(_selected.Key, Mathf.Max(1, _target), _quality); _totals = Materials.Totals(_tree); } } private void Rescan() { if ((Object)(object)Player.m_localPlayer == (Object)null) { _have = new Dictionary<string, int>(); } else { _have = StorageScanner.Scan(Mathf.Max(1f, CraftPlannerPlugin.ScanRadius.Value)); } } public void Draw() { //IL_0023: 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_0052: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!IsOpen) { return; } try { InitStyles(); _rect = GUILayout.Window(202047470, _rect, new WindowFunction(DrawWindow), Strings.T("Title") + " v0.1.0", Array.Empty<GUILayoutOption>()); } catch (Exception ex) { if (!_errorLogged) { _errorLogged = true; CraftPlannerPlugin.Log.LogError((object)("Planner UI error: " + ex)); } Close(); } } public void Prewarm() { try { InitStyles(); } catch { } } private void DrawWindow(int id) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (_data == null && (Object)(object)Player.m_localPlayer != (Object)null) { RefreshData(); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DrawList(); GUILayout.Space(8f); DrawDetails(); GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private void DrawList() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_011f: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label(Strings.T("Search"), _dim, Array.Empty<GUILayoutOption>()); string text = GUILayout.TextField(_search ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }); if (text != _search) { _search = text; ApplyFilter(); } GUILayout.Space(6f); _listScroll = GUILayout.BeginScrollView(_listScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_data == null) { GUILayout.Label(Strings.T("LoadWorld"), _node, Array.Empty<GUILayoutOption>()); } else { foreach (RecipeEntry item in _filtered) { bool flag = _selected != null && _selected.Key == item.Key; Color backgroundColor = GUI.backgroundColor; if (flag) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(item.Display, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { Select(item); } GUI.backgroundColor = backgroundColor; } if (_filtered.Count == 0) { GUILayout.Label(Strings.T("NothingFound"), _dim, Array.Empty<GUILayoutOption>()); } } GUILayout.EndScrollView(); GUILayout.Label(Strings.T("Close") + " • " + _filtered.Count + " " + Strings.T("Entries"), _dim, Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); } private void DrawDetails() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //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_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); _detailScroll = GUILayout.BeginScrollView(_detailScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_selected == null) { GUILayout.Label(Strings.T("Pick"), _node, Array.Empty<GUILayoutOption>()); } else { GUILayout.Label(_selected.Display, _title, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(_selected.Station)) { GUILayout.Label(Strings.T("Station") + " " + _selected.Station + ((_selected.StationLevel > 1) ? (" lvl " + _selected.StationLevel) : string.Empty), _dim, Array.Empty<GUILayoutOption>()); } if (!_selected.IsPiece && _selected.OutputAmount > 1) { GUILayout.Label(Strings.Format("Output", _selected.OutputAmount), _dim, Array.Empty<GUILayoutOption>()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Target"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _target = AmountButton(1, _target); _target = AmountButton(5, _target); _target = AmountButton(10, _target); _target = AmountButton(25, _target); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (_maxQuality > 1) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Quality"), _dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); for (int i = 1; i <= _maxQuality; i++) { Color backgroundColor = GUI.backgroundColor; if (i == _quality) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } if (GUILayout.Button(i.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) }) && i != _quality) { _quality = i; Rebuild(); } GUI.backgroundColor = backgroundColor; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(6f); GUILayout.Label(Strings.T("Ingredients"), _section, Array.Empty<GUILayoutOption>()); if (_tree != null) { DrawNode(_tree, 0); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(Strings.T("Raw"), _section, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button(Strings.T("Rescan"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) })) { Rescan(); } GUILayout.EndHorizontal(); DrawTotals(); } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private int AmountButton(int value, int current) { //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_001e: 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) Color backgroundColor = GUI.backgroundColor; if (current == value) { GUI.backgroundColor = new Color(0.42f, 0.62f, 0.42f); } int num = (GUILayout.Button("×" + value, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(46f) }) ? value : current); GUI.backgroundColor = backgroundColor; if (num != current) { Rebuild(); } return num; } private void DrawNode(MatNode node, int depth) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Space((float)depth * 18f); if (node.IsRaw) { GUI.color = ColorRaw; } else { GUI.color = Color.white; } GUILayout.Label(node.Display + " ×" + node.Amount, _node, Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(node.Note)) { GUI.color = ColorNote; GUILayout.Label("[" + NoteName(node.Note) + "]", _dim, Array.Empty<GUILayoutOption>()); } GUI.color = Color.white; if (node.IsRaw) { _have.TryGetValue(node.Key, out var value); GUI.color = ((value >= node.Amount) ? ColorOk : ColorMissing); GUILayout.Label(Strings.T("Have") + " " + value, _dim, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); foreach (MatNode child in node.Children) { DrawNode(child, depth + 1); } } private static string NoteName(string note) { return note switch { "build" => Strings.T("Build"), "craft" => Strings.T("Craft"), "smelted" => Strings.T("Smelted"), _ => note, }; } private void DrawTotals() { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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) if (_totals == null || _totals.Count == 0) { GUILayout.Label(Strings.T("NoMaterials"), _dim, Array.Empty<GUILayoutOption>()); return; } List<string> list = new List<string>(_totals.Keys); list.Sort((string a, string b) => string.Compare(_data.DisplayOf(a), _data.DisplayOf(b), StringComparison.CurrentCultureIgnoreCase)); foreach (string item in list) { int num = _totals[item]; _have.TryGetValue(item, out var value); GUI.color = ((value >= num) ? ColorOk : ColorMissing); GUILayout.Label(_data.DisplayOf(item) + " " + value + " / " + num, _node, Array.Empty<GUILayoutOption>()); GUI.color = Color.white; } } private void InitStyles() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0044: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //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) //IL_007d: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown if (_title == null) { _title = new GUIStyle(GUI.skin.label) { fontSize = 19, fontStyle = (FontStyle)1 }; _section = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)1 }; _node = new GUIStyle(GUI.skin.label) { fontSize = 13 }; GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 11 }; val.normal.textColor = new Color(0.72f, 0.72f, 0.72f); _dim = val; } } } [BepInPlugin("opencode.craftplanner", "Craft Planner", "0.1.0")] public class CraftPlannerPlugin : BaseUnityPlugin { public const string Guid = "opencode.craftplanner"; public const string Version = "0.1.0"; internal static CraftPlannerPlugin Instance; internal static ManualLogSource Log; internal static ConfigEntry<KeyCode> ToggleKey; internal static ConfigEntry<float> ScanRadius; internal static ConfigEntry<bool> DecomposeProcessing; internal static ConfigEntry<string> UiLanguage; private PlannerUI _ui; private Harmony _harmony; private void Awake() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)287, "Key that opens/closes the craft planner window."); ScanRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ScanRadius", 40f, "Radius in meters used when counting materials in nearby containers."); DecomposeProcessing = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DecomposeProcessing", true, "Show smelted bars and milled goods as their source material (ore, barley)."); UiLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "UiLanguage", "auto", new ConfigDescription("UI language: auto (follow the game), en or ru.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "auto", "en", "ru" }), Array.Empty<object>())); Strings.Refresh(); _ui = new PlannerUI(); _harmony = new Harmony("opencode.craftplanner"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Craft Planner v0.1.0 loaded. Toggle with " + ((object)ToggleKey.Value/*cast due to .constrained prefix*/).ToString())); } private void Update() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_ui != null) { if (Input.GetKeyDown(ToggleKey.Value) && !Menu.IsVisible() && !TextInput.IsVisible()) { _ui.Toggle(); } if (_ui.IsOpen) { _ui.Tick(); } } } private void OnGUI() { if (_ui != null) { if (!_ui.IsOpen) { _ui.Prewarm(); } _ui.Draw(); } } private void OnDestroy() { _ui?.Close(); _ui = null; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; } } public static class StorageScanner { public static Dictionary<string, int> Scan(float radius) { //IL_0035: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, int> result = new Dictionary<string, int>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return result; } Accumulate(((Humanoid)localPlayer).GetInventory(), result); Vector3 position = ((Component)localPlayer).transform.position; float num = radius * radius; Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); Container[] array2 = array; foreach (Container val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { Accumulate(val.GetInventory(), result); } } } return result; } private static void Accumulate(Inventory inventory, Dictionary<string, int> result) { if (inventory == null) { return; } foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_shared != null) { string name = allItem.m_shared.m_name; if (!string.IsNullOrEmpty(name)) { result.TryGetValue(name, out var value); result[name] = value + allItem.m_stack; } } } } } internal static class Strings { private static readonly Dictionary<string, string> En = new Dictionary<string, string> { { "Title", "Craft Planner" }, { "Search", "Search (ru/en)" }, { "NothingFound", "Nothing found." }, { "LoadWorld", "Load a world to use the planner." }, { "Pick", "Pick an item or a building piece on the left." }, { "Target", "Target:" }, { "Quality", "Quality:" }, { "Ingredients", "Ingredients" }, { "Raw", "Needed raw materials" }, { "Rescan", "Rescan containers" }, { "Close", "F6 to close" }, { "Entries", "entries" }, { "Station", "Station:" }, { "Output", "Output: x{0} per craft" }, { "Have", "have" }, { "NoMaterials", "No materials needed." }, { "Build", "build" }, { "Craft", "craft" }, { "Smelted", "smelted" } }; private static readonly Dictionary<string, string> Ru = new Dictionary<string, string> { { "Title", "Планировщик крафта" }, { "Search", "Поиск (рус/англ)" }, { "NothingFound", "Ничего не найдено." }, { "LoadWorld", "Загрузи мир, чтобы пользоваться планировщиком." }, { "Pick", "Выбери предмет или постройку слева." }, { "Target", "Нужно:" }, { "Quality", "Качество:" }, { "Ingredients", "Состав" }, { "Raw", "Нужно сырья" }, { "Rescan", "Обновить сундуки" }, { "Close", "F6 — закрыть" }, { "Entries", "позиций" }, { "Station", "Станция:" }, { "Output", "Выход: x{0} за крафт" }, { "Have", "есть" }, { "NoMaterials", "Материалы не нужны." }, { "Build", "постройка" }, { "Craft", "крафт" }, { "Smelted", "плавка" } }; public static bool IsRussian { get; private set; } public static void Refresh() { string a = ((CraftPlannerPlugin.UiLanguage != null) ? CraftPlannerPlugin.UiLanguage.Value : "auto"); if (string.Equals(a, "ru", StringComparison.OrdinalIgnoreCase)) { IsRussian = true; return; } if (string.Equals(a, "en", StringComparison.OrdinalIgnoreCase)) { IsRussian = false; return; } bool isRussian = false; try { Localization instance = Localization.instance; string text = ((instance != null) ? instance.GetSelectedLanguage() : null); isRussian = !string.IsNullOrEmpty(text) && text.IndexOf("Russian", StringComparison.OrdinalIgnoreCase) >= 0; } catch { } IsRussian = isRussian; } public static string T(string key) { Dictionary<string, string> dictionary = (IsRussian ? Ru : En); string value; return dictionary.TryGetValue(key, out value) ? value : key; } public static string Format(string key, object arg) { return string.Format(T(key), arg); } }