Decompiled source of GK2 Shopping List v0.6.12

GK2ShoppingList.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GK2.Framework;
using HarmonyLib;
using LazyBearTechnology;
using Newtonsoft.Json;
using Rewired;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace GK2ShoppingList
{
	internal static class Cm
	{
		public static ConfigDescription D(string description, string name, int order, bool advanced = false, AcceptableValueBase range = null, bool browsable = true)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes();
			configurationManagerAttributes.DispName = name;
			configurationManagerAttributes.Order = order;
			ConfigurationManagerAttributes configurationManagerAttributes2 = configurationManagerAttributes;
			if (advanced)
			{
				configurationManagerAttributes2.IsAdvanced = true;
			}
			if (!browsable)
			{
				configurationManagerAttributes2.Browsable = false;
			}
			return new ConfigDescription(description, range, new object[1] { configurationManagerAttributes2 });
		}
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput);

	public bool? ShowRangeAsPercent;

	public Action<ConfigEntryBase> CustomDrawer;

	public CustomHotkeyDrawerFunc CustomHotkeyDrawer;

	public bool? Browsable;

	public string Category;

	public object DefaultValue;

	public bool? HideDefaultButton;

	public bool? HideSettingName;

	public string Description;

	public string DispName;

	public int? Order;

	public bool? ReadOnly;

	public bool? IsAdvanced;

	public Func<object, string> ObjToStr;

	public Func<string, object> StrToObj;
}
namespace GK2ShoppingList
{
	internal static class ExtLoc
	{
		public static string Override = "";

		private static readonly Dictionary<string, Dictionary<string, string>> Loaded = new Dictionary<string, Dictionary<string, string>>();

		public static string Lang()
		{
			string text = (Override ?? "").Trim();
			if (text.Length > 0)
			{
				return text.ToLowerInvariant();
			}
			string text2 = null;
			try
			{
				text2 = LLBase.CurrentLang;
			}
			catch
			{
			}
			return (text2 ?? "en").ToLowerInvariant();
		}

		public static bool TryGet(string key, out string text)
		{
			return TryGet(Lang(), key, out text);
		}

		public static bool TryGet(string lang, string key, out string text)
		{
			text = null;
			if (key == null)
			{
				return false;
			}
			Dictionary<string, string> dictionary = For(lang);
			return dictionary != null && dictionary.TryGetValue(key, out text) && !string.IsNullOrEmpty(text);
		}

		public static string ModDir()
		{
			string location = typeof(ExtLoc).Assembly.Location;
			if (!string.IsNullOrEmpty(location))
			{
				return Path.GetDirectoryName(location);
			}
			return Path.Combine(Paths.PluginPath, "GK2ShoppingList");
		}

		public static string Folder()
		{
			try
			{
				return Path.Combine(ModDir(), "Localization");
			}
			catch
			{
				return null;
			}
		}

		private static Dictionary<string, string> For(string lang)
		{
			lang = (lang ?? "en").Trim().ToLowerInvariant();
			if (Loaded.TryGetValue(lang, out var value))
			{
				return value;
			}
			value = null;
			try
			{
				string text = Folder();
				if (text != null && Directory.Exists(text))
				{
					foreach (string item in Candidates(lang))
					{
						string text2 = Path.Combine(text, item + ".json");
						if (!File.Exists(text2))
						{
							continue;
						}
						value = Parse(File.ReadAllText(text2, Encoding.UTF8));
						Plugin.Log.LogInfo((object)("Localization: " + value.Count + " text(s) for '" + lang + "' from " + text2));
						break;
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Localization: could not read the file for '" + lang + "': " + ex.Message));
				value = null;
			}
			Loaded[lang] = value;
			return value;
		}

		private static IEnumerable<string> Candidates(string lang)
		{
			HashSet<string> seen = new HashSet<string>();
			string[] array = new string[4]
			{
				lang,
				lang.Replace('_', '-'),
				lang.Replace('-', '_'),
				(lang.Length <= 2) ? lang : lang.Substring(0, 2)
			};
			foreach (string c in array)
			{
				if (c.Length > 0 && seen.Add(c))
				{
					yield return c;
				}
			}
		}

		private static Dictionary<string, string> Parse(string s)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			int i = 0;
			Ws(s, ref i);
			if (i >= s.Length || s[i] != '{')
			{
				throw new FormatException("the file must be a JSON object { \"key\": \"text\", ... }");
			}
			i++;
			while (true)
			{
				Ws(s, ref i);
				if (i >= s.Length)
				{
					throw new FormatException("unexpected end of file");
				}
				if (s[i] == '}')
				{
					break;
				}
				if (s[i] == ',')
				{
					i++;
					continue;
				}
				string text = Str(s, ref i);
				Ws(s, ref i);
				if (i >= s.Length || s[i] != ':')
				{
					throw new FormatException("':' expected after \"" + text + "\"");
				}
				i++;
				Ws(s, ref i);
				if (i < s.Length && s[i] == '"')
				{
					dictionary[text] = Str(s, ref i);
				}
				else
				{
					Skip(s, ref i);
				}
			}
			return dictionary;
		}

		private static void Ws(string s, ref int i)
		{
			while (i < s.Length && (char.IsWhiteSpace(s[i]) || s[i] == '\ufeff'))
			{
				i++;
			}
		}

		private static string Str(string s, ref int i)
		{
			if (i >= s.Length || s[i] != '"')
			{
				throw new FormatException("a \"quoted\" string expected at " + i);
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length)
			{
				char c = s[i++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					continue;
				case '\\':
					break;
				}
				if (i >= s.Length)
				{
					break;
				}
				char c2 = s[i++];
				switch (c2)
				{
				case 'n':
					stringBuilder.Append('\n');
					break;
				case 't':
					stringBuilder.Append('\t');
					break;
				case 'r':
					stringBuilder.Append('\r');
					break;
				case 'b':
					stringBuilder.Append('\b');
					break;
				case 'f':
					stringBuilder.Append('\f');
					break;
				case 'u':
					if (i + 4 > s.Length)
					{
						throw new FormatException("bad \\u escape");
					}
					stringBuilder.Append((char)Convert.ToInt32(s.Substring(i, 4), 16));
					i += 4;
					break;
				default:
					stringBuilder.Append(c2);
					break;
				}
			}
			throw new FormatException("unterminated string");
		}

		private static void Skip(string s, ref int i)
		{
			int num = 0;
			while (i < s.Length)
			{
				switch (s[i])
				{
				case '"':
					Str(s, ref i);
					if (num == 0)
					{
						return;
					}
					continue;
				case '[':
				case '{':
					num++;
					break;
				case ']':
				case '}':
					if (num == 0)
					{
						return;
					}
					num--;
					break;
				case ',':
					if (num == 0)
					{
						return;
					}
					break;
				}
				i++;
			}
		}
	}
	internal static class Framework
	{
		private sealed class Entry : Gk2ModBase
		{
			private readonly Gk2ModMetadata _meta = new Gk2ModMetadata("gk2.shoppinglist", "GK2 Shopping List", "SaintArchI", "0.6.12", FwText.L("Remembers what you plan to build or craft and shows what to bring with you."), false, false);

			private readonly IReadOnlyList<Gk2ModDependency> _deps = (IReadOnlyList<Gk2ModDependency>)(object)new Gk2ModDependency[1]
			{
				new Gk2ModDependency("ru.superman4eg.gk2.framework", "0.1.0", "0.2.0", false)
			};

			private static readonly string[] PadChoices = new string[29]
			{
				"None", "RightStick", "LeftStick", "RightBumper", "RightTrigger", "LeftTrigger", "RightTrigger+RightStick", "RightTrigger+LeftStick", "LeftTrigger+RightStick", "LeftTrigger+LeftStick",
				"Fold", "Inventory", "ChangeWeapon", "Map", "QuestTree", "TechTree", "Inspirations", "Plant", "StartResurrection", "AlchemyBoost",
				"SaveImport", "AcceptVendorDeal", "MoveAllItemsFromPlayer", "MoveAllItemsToPlayer", "EndPrefight", "AttackFocus", "FoldAdditionalInfo", "PrevSubTab", "NextSubTab"
			};

			public override Gk2ModMetadata Metadata => _meta;

			public override IReadOnlyList<Gk2ModDependency> Dependencies => _deps;

			public override void OnRegister(Gk2ModContext context)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
				Gk2Settings settings = context.Settings;
				settings.AddKeybind("Hotkeys", "ToggleWindow", new KeyboardShortcut((KeyCode)283, Array.Empty<KeyCode>()), FwText.L("Open the list"), FwText.L("Opens and closes the shopping list window."), 0);
				settings.AddKeybind("Hotkeys", "ToggleHud", new KeyboardShortcut((KeyCode)283, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), FwText.L("Collapse the panel"), FwText.L("Collapses and expands the HUD panel."), 1);
				settings.AddKeybind("Hotkeys", "AddModifier", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), FwText.L("Add modifier"), FwText.L("Hold this and right-click a recipe to add it to the list."), 2);
				settings.AddKeybind("Hotkeys", "AddAllOrders", KeyboardShortcut.Empty, FwText.L("Add all town orders"), FwText.L("Adds every open town order to the list. Not set by default."), 3);
				settings.AddToggle("Window", "ByTask", true, FwText.L("Group by task"), FwText.L("The list window opens on the \"By task\" tab."), 3);
				settings.AddFloatSlider("HUD", "OffsetRight", 6f, 0f, 200f, FwText.L("Offset from the right"), FwText.L("Panel position, in game UI pixels."), 1f, 4);
				settings.AddFloatSlider("HUD", "OffsetTop", 36f, 0f, 200f, FwText.L("Offset from the top"), FwText.L("Panel position, in game UI pixels."), 1f, 5);
				settings.AddIntSlider("HUD", "MaxRows", 8, 1, 20, FwText.L("Rows on the panel"), FwText.L("How many ingredient rows the HUD panel shows."), 1, 6);
				settings.AddToggle("HUD", "ByTask", false, FwText.L("Group by task"), FwText.L("The HUD panel lists ingredients under the task they belong to instead of one total list."), 7);
				settings.AddToggle("HUD", "Collapsed", false, FwText.L("Panel collapsed"), FwText.L("The HUD panel is collapsed to its header."), 8);
				settings.AddToggle("General", "CountStorage", true, FwText.L("Count your chests"), FwText.L("Next to a missing ingredient, show how many more lie in your chests and storages (+N)."), 11);
				settings.AddFloatSlider("Window", "Scale", 0.85f, 0.5f, 1.5f, FwText.L("Window size"), FwText.L("Size of the list window. 1 is the size of the game's own windows."), 0.05f, 9);
				settings.AddFloatSlider("HUD", "Scale", 0.8f, 0.5f, 1.5f, FwText.L("Panel size"), FwText.L("Size of the HUD panel. 1 is the size of the game's own UI."), 0.05f, 10);
				settings.AddText("Gamepad", "AddKey", "RightStick", FwText.L("Add to the list"), FwText.L("Gamepad action that adds the focused recipe to the list, by the game's own name for it (RightStick, Fold, RightTrigger+RightStick for a chord...). Turn Log pad input on to see which actions each button of your pad sends."), 20);
				settings.AddText("Gamepad", "WindowKey", "LeftStick", FwText.L("Open the list"), FwText.L("Gamepad action that opens and closes the list window."), 21);
				settings.AddText("Gamepad", "ClearKey", "LeftTrigger+RightStick", FwText.L("Clear the list"), FwText.L("Gamepad action that clears the whole list: the first press asks, a second press within 4 seconds clears."), 21);
				settings.AddText("Gamepad", "HudKey", "RightTrigger+LeftStick", FwText.L("Collapse the panel"), FwText.L("Gamepad action that collapses and expands the HUD panel."), 21);
				settings.AddToggle("Gamepad", "ExclusivePlain", true, FwText.L("Leave chords alone"), FwText.L("A binding without a chord stays quiet while a trigger or R1 is held, so another mod can use that same button as a chord."), 22);
				settings.AddToggle("Gamepad", "LogInput", false, FwText.L("Log pad input"), FwText.L("Writes every gamepad action to BepInEx/LogOutput.log, so a free button can be found."), 23);
			}
		}

		public const string FrameworkGuid = "ru.superman4eg.gk2.framework";

		public static void TryRegister(BaseUnityPlugin plugin)
		{
			try
			{
				if (AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "GK2.Framework"))
				{
					Register(plugin);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogInfo((object)("Mod Framework not used: " + ex.Message));
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static void Register(BaseUnityPlugin plugin)
		{
			foreach (RegisteredMod mod in FrameworkApi.Mods)
			{
				if (mod != null && mod.Metadata != null && string.Equals(mod.Metadata.Id, "gk2.shoppinglist", StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
			}
			FrameworkApi.RegisterMod((IGk2Mod)(object)new Entry(), plugin.Config);
			Plugin.Log.LogInfo((object)"Registered in GK2 Mod Framework");
		}
	}
	internal static class FwText
	{
		private static readonly string[] Langs = new string[10] { "ru", "de", "fr", "es", "pt", "pl", "tr", "ja", "zh", "ko" };

		private static int _idx = -2;

		private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]>
		{
			{
				"Log pad input",
				new string[10] { "Писать кнопки геймпада в лог", "Gamepad-Eingaben protokollieren", "Journaliser la manette", "Registrar el mando", "Registrar o controle", "Zapisuj pad do logu", "Gamepad girdilerini kaydet", "パッド入力をログに記録", "记录手柄输入", "패드 입력 기록" }
			},
			{
				"Writes every gamepad action to BepInEx/LogOutput.log, so a free button can be found.",
				new string[10] { "Пишет каждое действие геймпада в BepInEx/LogOutput.log, чтобы найти свободную кнопку.", "Schreibt jede Gamepad-Aktion in BepInEx/LogOutput.log, damit sich eine freie Taste finden lässt.", "Écrit chaque action de la manette dans BepInEx/LogOutput.log, pour trouver un bouton libre.", "Escribe cada acción del mando en BepInEx/LogOutput.log para encontrar un botón libre.", "Grava cada ação do controle em BepInEx/LogOutput.log, para achar um botão livre.", "Zapisuje każdą akcję pada w BepInEx/LogOutput.log, żeby znaleźć wolny przycisk.", "Boş bir tuş bulmak için her gamepad eylemini BepInEx/LogOutput.log dosyasına yazar.", "空いているボタンを探せるよう、パッドの操作をすべて BepInEx/LogOutput.log に書き出します。", "将手柄的每个动作写入 BepInEx/LogOutput.log,便于找到空闲按键。", "빈 버튼을 찾을 수 있도록 패드의 모든 동작을 BepInEx/LogOutput.log에 기록합니다." }
			},
			{
				"Leave chords alone",
				new string[10] { "Не мешать сочетаниям", "Kombinationen freilassen", "Laisser les combinaisons", "Respetar combinaciones", "Respeitar combinações", "Nie przeszkadzaj kombinacjom", "Kombinasyonlara dokunma", "組み合わせを優先", "不干扰组合键", "조합키 비켜주기" }
			},
			{
				"A binding without a chord stays quiet while a trigger or R1 is held, so another mod can use that same button as a chord.",
				new string[10] { "Кнопка без сочетания молчит, пока зажат курок или R1, — тогда другой мод может использовать её в сочетании.", "Eine Belegung ohne Kombination schweigt, solange ein Trigger oder R1 gehalten wird, damit ein anderer Mod dieselbe Taste als Kombination nutzen kann.", "Une touche sans combinaison ne réagit pas tant qu'une gâchette ou R1 est enfoncée, pour qu'un autre mod l'utilise en combinaison.", "Un botón sin combinación no reacciona mientras se mantiene un gatillo o R1, así otro mod puede usarlo en combinación.", "Um botão sem combinação não reage enquanto um gatilho ou R1 estiver pressionado, para outro mod usá-lo em combinação.", "Przycisk bez kombinacji milczy, gdy wciśnięty jest spust lub R1, więc inny mod może go użyć w kombinacji.", "Kombinasyonsuz bir atama, tetik veya R1 basılıyken çalışmaz; böylece başka bir mod aynı tuşu kombinasyonda kullanabilir.", "トリガーやR1を押している間は単独割り当てが反応しないので、他のMODが同じボタンを組み合わせで使えます。", "按住扳机或 R1 时,单键绑定不响应,这样其他模组可以把同一按键用作组合键。", "트리거나 R1을 누르고 있는 동안 단독 할당은 반응하지 않아, 다른 모드가 같은 버튼을 조합키로 쓸 수 있습니다." }
			},
			{
				"Add all town orders",
				new string[10] { "Добавить все заказы", "Alle Stadtaufträge hinzufügen", "Ajouter toutes les commandes", "Añadir todos los encargos", "Adicionar todas as encomendas", "Dodaj wszystkie zamówienia", "Tüm siparişleri ekle", "注文をすべて追加", "添加所有订单", "모든 주문 추가" }
			},
			{
				"Adds every open town order to the list. Not set by default.",
				new string[10] { "Добавляет в список все открытые заказы города. По умолчанию не задана.", "Fügt alle offenen Stadtaufträge zur Liste hinzu. Standardmäßig nicht belegt.", "Ajoute à la liste toutes les commandes en cours de la ville. Non définie par défaut.", "Añade a la lista todos los encargos abiertos del pueblo. Sin asignar por defecto.", "Adiciona à lista todas as encomendas abertas da cidade. Sem tecla por padrão.", "Dodaje do listy wszystkie otwarte zamówienia miasta. Domyślnie nieprzypisany.", "Kasabanın tüm açık siparişlerini listeye ekler. Varsayılan olarak atanmamış.", "受けている町の注文をすべてリストに追加します。既定では未設定です。", "把城里所有进行中的订单加入清单。默认未设置。", "진행 중인 마을 주문을 모두 목록에 추가합니다. 기본값은 지정 안 됨." }
			},
			{
				"Clear the list",
				new string[10] { "Очистить список", "Liste leeren", "Vider la liste", "Vaciar la lista", "Limpar a lista", "Wyczyść listę", "Listeyi temizle", "リストを消去", "清空清单", "목록 지우기" }
			},
			{
				"Gamepad action that clears the whole list: the first press asks, a second press within 4 seconds clears.",
				new string[10] { "Действие геймпада, которое очищает весь список: первое нажатие спрашивает, второе в течение 4 секунд очищает.", "Gamepad-Aktion, die die ganze Liste leert: der erste Druck fragt nach, ein zweiter innerhalb von 4 Sekunden leert sie.", "Action de la manette qui vide toute la liste : le premier appui demande, un second dans les 4 secondes la vide.", "Acción del mando que vacía toda la lista: la primera pulsación pregunta y una segunda en 4 segundos la vacía.", "Ação do controle que limpa a lista inteira: o primeiro toque pergunta, um segundo em até 4 segundos limpa.", "Akcja pada, która czyści całą listę: pierwsze naciśnięcie pyta, drugie w ciągu 4 sekund czyści.", "Tüm listeyi temizleyen oyun kolu eylemi: ilk basış sorar, 4 saniye içindeki ikinci basış temizler.", "リスト全体を消去するゲームパッド操作。1回目で確認し、4秒以内にもう一度押すと消去します。", "清空整个清单的手柄操作:第一次按下会确认,4秒内再按一次即清空。", "목록 전체를 지우는 게임패드 동작: 첫 번째 누르면 확인하고, 4초 안에 다시 누르면 지웁니다." }
			},
			{
				"Gamepad action that collapses and expands the HUD panel.",
				new string[10] { "Действие геймпада, которое сворачивает и разворачивает панель на экране.", "Gamepad-Aktion, die die Bildschirmleiste ein- und ausklappt.", "Action de la manette qui replie et déplie le panneau à l'écran.", "Acción del mando que pliega y despliega el panel en pantalla.", "Ação do controle que recolhe e expande o painel na tela.", "Akcja pada, która zwija i rozwija panel na ekranie.", "Ekrandaki paneli daraltıp genişleten oyun kolu eylemi.", "画面のパネルを折りたたんだり広げたりするゲームパッド操作。", "折叠和展开屏幕面板的手柄操作。", "화면 패널을 접고 펼치는 게임패드 동작." }
			},
			{
				"Open the list",
				new string[10] { "Открыть список", "Liste öffnen", "Ouvrir la liste", "Abrir la lista", "Abrir a lista", "Otwórz listę", "Listeyi aç", "リストを開く", "打开清单", "목록 열기" }
			},
			{
				"Gamepad action that opens and closes the list window.",
				new string[10] { "Действие геймпада, которое открывает и закрывает окно списка.", "Gamepad-Aktion, die das Listenfenster öffnet und schließt.", "Action de la manette qui ouvre et ferme la fenêtre de la liste.", "Acción del mando que abre y cierra la ventana de la lista.", "Ação do controle que abre e fecha a janela da lista.", "Akcja pada, która otwiera i zamyka okno listy.", "Liste penceresini açıp kapatan gamepad eylemi.", "リストウィンドウを開閉するパッドの操作。", "打开和关闭清单窗口的手柄动作。", "목록 창을 열고 닫는 패드 동작입니다." }
			},
			{
				"Add to the list",
				new string[10] { "Добавить в список", "Zur Liste hinzufügen", "Ajouter à la liste", "Añadir a la lista", "Adicionar à lista", "Dodaj do listy", "Listeye ekle", "リストに追加", "加入清单", "목록에 추가" }
			},
			{
				"Gamepad action that adds the focused recipe to the list, by the game's own name for it (RightStick, Fold, RightTrigger+RightStick for a chord...). Turn Log pad input on to see which actions each button of your pad sends.",
				new string[10] { "Действие геймпада, которое добавляет выбранный рецепт в список, по названию из самой игры (RightStick, Fold, RightTrigger+RightStick для сочетания…). Включите «Писать кнопки геймпада в лог», чтобы увидеть, какие действия шлёт каждая кнопка.", "Gamepad-Aktion, die das markierte Rezept zur Liste hinzufügt, mit dem spieleigenen Namen (RightStick, Fold, RightTrigger+RightStick für eine Kombination …). Schalte „Gamepad-Eingaben protokollieren“ ein, um zu sehen, welche Aktionen jede Taste sendet.", "Action de la manette qui ajoute la recette sélectionnée à la liste, sous le nom que lui donne le jeu (RightStick, Fold, RightTrigger+RightStick pour une combinaison…). Activez « Journaliser la manette » pour voir ce qu'envoie chaque bouton.", "Acción del mando que añade la receta seleccionada a la lista, con el nombre que le da el juego (RightStick, Fold, RightTrigger+RightStick para una combinación…). Activa «Registrar el mando» para ver qué envía cada botón.", "Ação do controle que adiciona a receita selecionada à lista, pelo nome que o jogo lhe dá (RightStick, Fold, RightTrigger+RightStick para uma combinação…). Ative «Registrar o controle» para ver o que cada botão envia.", "Akcja pada, która dodaje zaznaczony przepis do listy, pod nazwą z gry (RightStick, Fold, RightTrigger+RightStick dla kombinacji…). Włącz „Zapisuj pad do logu”, by zobaczyć, co wysyła każdy przycisk.", "Seçili tarifi listeye ekleyen gamepad eylemi, oyunun verdiği adla (RightStick, Fold, kombinasyon için RightTrigger+RightStick…). Her tuşun ne gönderdiğini görmek için «Gamepad girdilerini kaydet»i açın.", "選択中のレシピをリストに追加するパッドの操作(ゲーム内の名前で指定:RightStick、Fold、組み合わせは RightTrigger+RightStick など)。各ボタンが何を送るかは「パッド入力をログに記録」をオンにすると分かります。", "把当前选中的配方加入清单的手柄动作,使用游戏自己的名称(RightStick、Fold,组合键如 RightTrigger+RightStick…)。打开“记录手柄输入”可查看每个按键发送的动作。", "선택한 레시피를 목록에 추가하는 패드 동작으로, 게임에서 쓰는 이름으로 지정합니다(RightStick, Fold, 조합키는 RightTrigger+RightStick 등). 각 버튼이 보내는 동작은 「패드 입력 기록」을 켜면 볼 수 있습니다." }
			},
			{
				"Panel size",
				new string[10] { "Размер панели", "Panelgröße", "Taille du panneau", "Tamaño del panel", "Tamanho do painel", "Rozmiar panelu", "Panel boyutu", "パネルの大きさ", "面板大小", "패널 크기" }
			},
			{
				"Size of the HUD panel. 1 is the size of the game's own UI.",
				new string[10] { "Размер панели на экране. 1 — размер интерфейса самой игры.", "Größe des HUD-Panels. 1 entspricht der Oberfläche des Spiels.", "Taille du panneau à l'écran. 1 correspond à l'interface du jeu.", "Tamaño del panel en pantalla. 1 es el tamaño de la interfaz del juego.", "Tamanho do painel na tela. 1 é o tamanho da interface do jogo.", "Rozmiar panelu na ekranie. 1 to rozmiar interfejsu gry.", "Ekrandaki panelin boyutu. 1, oyunun kendi arayüz boyutudur.", "画面上のパネルの大きさ。1 でゲーム本来のUIと同じ大きさ。", "屏幕面板的大小。1 为游戏自身界面的大小。", "화면 패널의 크기입니다. 1은 게임 UI와 같은 크기입니다." }
			},
			{
				"Window size",
				new string[10] { "Размер окна", "Fenstergröße", "Taille de la fenêtre", "Tamaño de la ventana", "Tamanho da janela", "Rozmiar okna", "Pencere boyutu", "ウィンドウの大きさ", "窗口大小", "창 크기" }
			},
			{
				"Size of the list window. 1 is the size of the game's own windows.",
				new string[10] { "Размер окна списка. 1 — размер окон самой игры.", "Größe des Listenfensters. 1 entspricht den Fenstern des Spiels.", "Taille de la fenêtre de la liste. 1 correspond aux fenêtres du jeu.", "Tamaño de la ventana de la lista. 1 es el tamaño de las ventanas del juego.", "Tamanho da janela da lista. 1 é o tamanho das janelas do jogo.", "Rozmiar okna listy. 1 to rozmiar okien gry.", "Liste penceresinin boyutu. 1, oyunun kendi pencereleriyle aynıdır.", "リストウィンドウの大きさ。1 でゲーム本来のウィンドウと同じ大きさ。", "清单窗口的大小。1 为游戏自身窗口的大小。", "목록 창의 크기입니다. 1은 게임 창과 같은 크기입니다." }
			},
			{
				"Count your chests",
				new string[10] { "Учитывать сундуки", "Truhen mitzählen", "Compter les coffres", "Contar los cofres", "Contar os baús", "Licz skrzynie", "Sandıkları say", "チェストも数える", "统计箱子", "상자도 세기" }
			},
			{
				"Next to a missing ingredient, show how many more lie in your chests and storages (+N).",
				new string[10] { "Рядом с недостающим ингредиентом показывать, сколько ещё лежит в ваших сундуках и хранилищах (+N).", "Neben einer fehlenden Zutat anzeigen, wie viele noch in deinen Truhen und Lagern liegen (+N).", "À côté d'un ingrédient manquant, afficher combien il en reste dans vos coffres et réserves (+N).", "Junto a un ingrediente que falta, mostrar cuántos más hay en tus cofres y almacenes (+N).", "Ao lado de um ingrediente em falta, mostrar quantos mais há nos seus baús e depósitos (+N).", "Przy brakującym składniku pokazuj, ile jeszcze leży w twoich skrzyniach i magazynach (+N).", "Eksik bir malzemenin yanında sandıklarında ve depolarında kaç tane daha olduğunu göster (+N).", "足りない材料の横に、チェストや倉庫にあと何個あるかを表示します(+N)。", "在缺少的材料旁显示你的箱子和仓库里还有多少(+N)。", "부족한 재료 옆에 상자와 창고에 몇 개가 더 있는지 표시합니다(+N)." }
			},
			{
				"Panel collapsed",
				new string[10] { "Панель свёрнута", "Panel eingeklappt", "Panneau replié", "Panel plegado", "Painel recolhido", "Panel zwinięty", "Panel daraltılmış", "パネルを折りたたむ", "面板已折叠", "패널 접기" }
			},
			{
				"The HUD panel is collapsed to its header.",
				new string[10] { "Панель на экране свёрнута до заголовка.", "Das HUD-Panel ist auf die Kopfzeile eingeklappt.", "Le panneau est replié sur son titre.", "El panel queda plegado a su título.", "O painel fica recolhido ao título.", "Panel na ekranie jest zwinięty do nagłówka.", "Ekrandaki panel başlığına kadar daraltılır.", "画面のパネルを見出しだけに折りたたみます。", "屏幕面板折叠为仅显示标题。", "화면 패널을 제목만 남기고 접습니다." }
			},
			{
				"Group by task",
				new string[10] { "По задачам", "Nach Aufgabe gruppieren", "Grouper par tâche", "Agrupar por tarea", "Agrupar por tarefa", "Grupuj według zadań", "Göreve göre grupla", "タスク別に表示", "按任务分组", "작업별로 묶기" }
			},
			{
				"The HUD panel lists ingredients under the task they belong to instead of one total list.",
				new string[10] { "Панель показывает ингредиенты под задачей, к которой они относятся, а не общим списком.", "Das HUD-Panel zeigt die Zutaten unter ihrer Aufgabe statt in einer Gesamtliste.", "Le panneau range les ingrédients sous leur tâche au lieu d'une liste totale.", "El panel muestra los ingredientes bajo su tarea en vez de una lista total.", "O painel mostra os ingredientes sob a tarefa a que pertencem, em vez de uma lista total.", "Panel pokazuje składniki pod zadaniem, do którego należą, zamiast jednej listy.", "Panel, malzemeleri tek bir liste yerine ait oldukları görevin altında gösterir.", "パネルで材料を合計ではなく、それぞれのタスクの下に表示します。", "面板按所属任务列出材料,而不是一个总清单。", "패널이 재료를 하나의 합계 목록 대신 해당 작업 아래에 보여줍니다." }
			},
			{
				"Rows on the panel",
				new string[10] { "Строк на панели", "Zeilen im Panel", "Lignes du panneau", "Filas del panel", "Linhas do painel", "Wiersze na panelu", "Paneldeki satırlar", "パネルの行数", "面板行数", "패널 줄 수" }
			},
			{
				"How many ingredient rows the HUD panel shows.",
				new string[10] { "Сколько строк с ингредиентами показывает панель.", "Wie viele Zutatenzeilen das HUD-Panel zeigt.", "Nombre de lignes d'ingrédients affichées par le panneau.", "Cuántas filas de ingredientes muestra el panel.", "Quantas linhas de ingredientes o painel mostra.", "Ile wierszy ze składnikami pokazuje panel.", "Panelin gösterdiği malzeme satırı sayısı.", "パネルに表示する材料の行数。", "面板显示的材料行数。", "패널에 표시할 재료 줄 수입니다." }
			},
			{
				"Offset from the top",
				new string[10] { "Отступ сверху", "Abstand von oben", "Décalage depuis le haut", "Distancia desde arriba", "Distância do topo", "Odstęp od góry", "Üstten uzaklık", "上からの距離", "距顶部", "위쪽 여백" }
			},
			{
				"Panel position, in game UI pixels.",
				new string[10] { "Положение панели, в пикселях интерфейса игры.", "Position des Panels, in Pixeln der Spieloberfläche.", "Position du panneau, en pixels de l'interface du jeu.", "Posición del panel, en píxeles de la interfaz del juego.", "Posição do painel, em pixels da interface do jogo.", "Położenie panelu, w pikselach interfejsu gry.", "Panelin konumu, oyun arayüzü pikseli cinsinden.", "パネルの位置(ゲームUIのピクセル単位)。", "面板位置,以游戏界面像素为单位。", "패널 위치(게임 UI 픽셀 단위)입니다." }
			},
			{
				"Offset from the right",
				new string[10] { "Отступ справа", "Abstand von rechts", "Décalage depuis la droite", "Distancia desde la derecha", "Distância da direita", "Odstęp od prawej", "Sağdan uzaklık", "右からの距離", "距右侧", "오른쪽 여백" }
			},
			{
				"The list window opens on the \"By task\" tab.",
				new string[10] { "Окно списка открывается на вкладке «По задачам».", "Das Listenfenster öffnet sich auf dem Tab „Nach Aufgabe“.", "La fenêtre de la liste s'ouvre sur l'onglet « Par tâche ».", "La ventana de la lista se abre en la pestaña «Por tarea».", "A janela da lista abre na aba «Por tarefa».", "Okno listy otwiera się na karcie „Według zadań”.", "Liste penceresi «Göreve göre» sekmesinde açılır.", "リストウィンドウを「タスク別」タブで開きます。", "清单窗口打开时显示“按任务”标签页。", "목록 창이 「작업별」 탭으로 열립니다." }
			},
			{
				"Add modifier",
				new string[10] { "Клавиша добавления", "Hinzufügen-Taste", "Touche d'ajout", "Tecla para añadir", "Tecla para adicionar", "Klawisz dodawania", "Ekleme tuşu", "追加キー", "添加键", "추가 키" }
			},
			{
				"Hold this and right-click a recipe to add it to the list.",
				new string[10] { "Зажмите её и щёлкните рецепт правой кнопкой, чтобы добавить его в список.", "Halte sie gedrückt und klicke mit Rechtsklick auf ein Rezept, um es zur Liste hinzuzufügen.", "Maintenez-la et faites un clic droit sur une recette pour l'ajouter à la liste.", "Mantenla pulsada y haz clic derecho en una receta para añadirla a la lista.", "Segure-a e clique com o botão direito numa receita para adicioná-la à lista.", "Przytrzymaj go i kliknij prawym przyciskiem przepis, by dodać go do listy.", "Bunu basılı tutup bir tarife sağ tıklayarak listeye ekleyin.", "このキーを押しながらレシピを右クリックするとリストに追加されます。", "按住此键并右键点击配方,即可加入清单。", "이 키를 누른 채 레시피를 우클릭하면 목록에 추가됩니다." }
			},
			{
				"Collapse the panel",
				new string[10] { "Свернуть панель", "Panel einklappen", "Replier le panneau", "Plegar el panel", "Recolher o painel", "Zwiń panel", "Paneli daralt", "パネルを折りたたむ", "折叠面板", "패널 접기" }
			},
			{
				"Collapses and expands the HUD panel.",
				new string[10] { "Сворачивает и разворачивает панель на экране.", "Klappt das HUD-Panel ein und aus.", "Replie et déplie le panneau.", "Pliega y despliega el panel.", "Recolhe e expande o painel.", "Zwija i rozwija panel na ekranie.", "Ekrandaki paneli daraltır ve genişletir.", "画面のパネルを折りたたみ・展開します。", "折叠或展开屏幕面板。", "화면 패널을 접고 펼칩니다." }
			},
			{
				"Opens and closes the shopping list window.",
				new string[10] { "Открывает и закрывает окно списка покупок.", "Öffnet und schließt das Fenster der Einkaufsliste.", "Ouvre et ferme la fenêtre de la liste de courses.", "Abre y cierra la ventana de la lista de compras.", "Abre e fecha a janela da lista de compras.", "Otwiera i zamyka okno listy zakupów.", "Alışveriş listesi penceresini açar ve kapatır.", "買い物リストのウィンドウを開閉します。", "打开和关闭购物清单窗口。", "쇼핑 목록 창을 열고 닫습니다." }
			},
			{
				"Remembers what you plan to build or craft and shows what to bring with you.",
				new string[10] { "Запоминает, что вы собираетесь построить или скрафтить, и показывает, что взять с собой.", "Merkt sich, was du bauen oder herstellen willst, und zeigt, was du mitnehmen musst.", "Retient ce que vous comptez construire ou fabriquer et montre ce qu'il faut emporter.", "Recuerda lo que planeas construir o fabricar y muestra lo que debes llevar.", "Lembra o que você planeja construir ou fabricar e mostra o que levar.", "Pamięta, co planujesz zbudować lub wytworzyć, i pokazuje, co zabrać ze sobą.", "İnşa etmeyi veya üretmeyi planladığın şeyleri hatırlar ve yanına ne alman gerektiğini gösterir.", "建てたい・作りたいものを覚えておき、持っていく物を表示します。", "记住你打算建造或制作的东西,并显示需要随身携带什么。", "짓거나 만들 계획인 것을 기억하고 챙겨야 할 것을 보여줍니다." }
			}
		};

		private static int Index
		{
			get
			{
				if (_idx != -2)
				{
					return _idx;
				}
				string text = null;
				text = FileLang();
				text = (text ?? "en").ToLowerInvariant();
				_idx = -1;
				for (int i = 0; i < Langs.Length; i++)
				{
					if (text.StartsWith(Langs[i]))
					{
						_idx = i;
						break;
					}
				}
				return _idx;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private static string FrameworkLanguage()
		{
			return FrameworkLocalization.CurrentLanguage;
		}

		private static string FileLang()
		{
			string text = (ExtLoc.Override ?? "").Trim();
			if (text.Length > 0)
			{
				return text;
			}
			string text2 = null;
			try
			{
				text2 = FrameworkLanguage();
			}
			catch
			{
			}
			return text2 ?? "en";
		}

		public static string L(string en)
		{
			if (en != null && ExtLoc.TryGet(FileLang(), en, out var text))
			{
				return text;
			}
			int index = Index;
			if (index < 0 || en == null || !Table.TryGetValue(en, out var value) || index >= value.Length || string.IsNullOrEmpty(value[index]))
			{
				return en;
			}
			return value[index];
		}
	}
	internal static class GameApi
	{
		private static readonly Dictionary<string, List<ItemDef>> _groupCache = new Dictionary<string, List<ItemDef>>();

		private static readonly HashSet<string> NotStorage = new HashSet<string> { "survey_wgo", "town_supplies", "well_garden_1", "well_garden_2" };

		private static Dictionary<string, Dictionary<string, int>> _stored;

		private static float _storedAt = -100f;

		internal const string QuestIcon = "default_quest_icon";

		private static readonly Dictionary<string, string> _nameCache = new Dictionary<string, string>();

		private static string _nameCacheLang;

		private static readonly KeyValuePair<string, string>[] Families = new KeyValuePair<string, string>[3]
		{
			new KeyValuePair<string, string>("blockage", "base_blockage_1"),
			new KeyValuePair<string, string>("pins_repair", "conveyor_place_pin_broken"),
			new KeyValuePair<string, string>("place_pins", "conveyor_place_pin_broken")
		};

		public static bool InGame()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			try
			{
				MainGame instance = MainGame.Instance;
				return (Object)(object)instance != (Object)null && (int)instance.gameState == 1 && MainGame.PlayerData != null;
			}
			catch
			{
				return false;
			}
		}

		public static string SlotKey()
		{
			try
			{
				string text = ((!((Object)(object)MainGame.Instance != (Object)null)) ? null : MainGame.Instance.SaveSlotData)?.slotName;
				if (string.IsNullOrEmpty(text))
				{
					return null;
				}
				return Regex.Replace(text, "_backup_\\d+$", "");
			}
			catch
			{
				return null;
			}
		}

		public static string Lang()
		{
			try
			{
				return LLBase.CurrentLang ?? "";
			}
			catch
			{
				return "";
			}
		}

		public static bool IsRussian()
		{
			try
			{
				return LLBase.CurrentLang?.StartsWith("ru") ?? false;
			}
			catch
			{
				return false;
			}
		}

		public static string L(string key)
		{
			try
			{
				return (!LLBase.HasL(key)) ? null : LLBase.L(key);
			}
			catch
			{
				return null;
			}
		}

		public static ItemDef ItemDef(string id)
		{
			try
			{
				return ((GameBalanceBase)GameBalance.Me).GetDataOrNull<ItemDef>(id);
			}
			catch
			{
				return null;
			}
		}

		public static string ItemName(string id, bool isGroup)
		{
			if (isGroup)
			{
				string text = null;
				bool flag = true;
				foreach (ItemDef item in GroupDefs(id))
				{
					string text2 = null;
					try
					{
						text2 = item.GetHeader();
					}
					catch
					{
					}
					if (!string.IsNullOrEmpty(text2) && !(text2 == ((BalanceBaseObject)item).id))
					{
						if (text == null)
						{
							text = text2;
						}
						else if (text2 != text)
						{
							flag = false;
						}
					}
				}
				if (text != null)
				{
					return (!flag) ? (text + "…") : text;
				}
			}
			ItemDef val = ((!isGroup) ? ItemDef(id) : null);
			if (val != null)
			{
				try
				{
					string header = val.GetHeader();
					if (!string.IsNullOrEmpty(header))
					{
						return header;
					}
				}
				catch
				{
				}
			}
			return L(id) ?? id;
		}

		public static string ItemIcon(string id, bool isGroup)
		{
			if (isGroup)
			{
				List<ItemDef> list = GroupDefs(id);
				return (list.Count <= 0) ? null : list[0].iconId;
			}
			return ItemDef(id)?.iconId;
		}

		public static string StarIcon(string id, bool isGroup)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			if (isGroup || string.IsNullOrEmpty(id))
			{
				return null;
			}
			ItemDef val = ItemDef(id);
			try
			{
				return (val == null || (int)val.qualityType != 1 || val.quality <= 0) ? null : ("item_star_" + val.quality);
			}
			catch
			{
				return null;
			}
		}

		public static Sprite Sprite(string iconId)
		{
			if (string.IsNullOrEmpty(iconId))
			{
				return null;
			}
			try
			{
				return LazySingletonSO<EasySpritesCollection>.Instance.GetSprite(iconId, (string)null);
			}
			catch
			{
				return null;
			}
		}

		public static List<ItemDef> GroupDefs(string groupNeedId)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			if (_groupCache.TryGetValue(groupNeedId, out var value))
			{
				return value;
			}
			value = new List<ItemDef>();
			try
			{
				NeedItemData val = new NeedItemData(groupNeedId, 1);
				List<ItemDef> list = default(List<ItemDef>);
				if (val.TryGetGroupItemDefs(ref list) && list != null)
				{
					value = list;
				}
			}
			catch
			{
			}
			_groupCache[groupNeedId] = value;
			return value;
		}

		public static int BackpackCount(string id, bool isGroup)
		{
			try
			{
				Item data = MainGame.PlayerData.Inventory.Data;
				if (data == null)
				{
					return 0;
				}
				if (!isGroup)
				{
					return data.GetTotalCountInInventory(id, (Item)null, false);
				}
				int num = 0;
				foreach (ItemDef item in GroupDefs(id))
				{
					num += data.GetTotalCountInInventory(((BalanceBaseObject)item).id, (Item)null, false);
				}
				return num;
			}
			catch
			{
				return 0;
			}
		}

		public static int StorageCount(string id, bool isGroup)
		{
			int num = 0;
			foreach (KeyValuePair<string, int> item in StorageByZone(id, isGroup))
			{
				num += item.Value;
			}
			return num;
		}

		public static List<KeyValuePair<string, int>> StorageByZone(string id, bool isGroup)
		{
			Dictionary<string, Dictionary<string, int>> s = StoredSnapshot();
			Dictionary<string, int> zones = new Dictionary<string, int>();
			Action<string> action = delegate(string itemId)
			{
				if (itemId == null || !s.TryGetValue(itemId, out var value))
				{
					return;
				}
				foreach (KeyValuePair<string, int> item in value)
				{
					zones[item.Key] = (zones.TryGetValue(item.Key, out var value2) ? value2 : 0) + item.Value;
				}
			};
			if (!isGroup)
			{
				action(id);
			}
			else
			{
				foreach (ItemDef item2 in GroupDefs(id))
				{
					if (item2 != null)
					{
						action(((BalanceBaseObject)item2).id);
					}
				}
			}
			return zones.OrderByDescending((KeyValuePair<string, int> kv) => kv.Value).ToList();
		}

		public static string ZoneName(string zoneId)
		{
			if (string.IsNullOrEmpty(zoneId))
			{
				return L10n.T("Elsewhere");
			}
			return L("wz_" + zoneId) ?? zoneId;
		}

		public static string StorageText(string id, bool isGroup, int maxZones)
		{
			List<KeyValuePair<string, int>> list = StorageByZone(id, isGroup);
			if (list.Count == 0)
			{
				return null;
			}
			List<string> list2 = (from z in list.Take(maxZones)
				select "+" + z.Value + " " + ZoneName(z.Key)).ToList();
			string text = string.Join(", ", list2.ToArray());
			if (list.Count > maxZones)
			{
				text += " …";
			}
			return text;
		}

		private static Dictionary<string, Dictionary<string, int>> StoredSnapshot()
		{
			if (_stored != null && Time.unscaledTime - _storedAt < 2f)
			{
				return _stored;
			}
			Dictionary<string, Dictionary<string, int>> dictionary = new Dictionary<string, Dictionary<string, int>>();
			try
			{
				WorldData worldData = MainGame.Instance.GameSave.worldData;
				foreach (GameSceneData gameSceneData in worldData.gameSceneDataList)
				{
					if (gameSceneData == null || gameSceneData.wgoDataList == null)
					{
						continue;
					}
					foreach (WgoData wgoData in gameSceneData.wgoDataList)
					{
						if (wgoData == null || string.IsNullOrEmpty(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id) || NotStorage.Contains(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id))
						{
							continue;
						}
						WGODef val = null;
						try
						{
							val = ((ObjectLinkedToDefinition<WGODef>)(object)wgoData).Definition;
						}
						catch
						{
						}
						if (val == null || !val.OpenInMultiInventory)
						{
							continue;
						}
						Inventory val2 = null;
						try
						{
							val2 = wgoData.Inventory;
						}
						catch
						{
						}
						if (val2 == null || val2.Data == null)
						{
							continue;
						}
						string zone = "";
						try
						{
							WorldZoneData worldZoneData = wgoData.WorldZoneData;
							if (worldZoneData != null && ((ObjectLinkedToDefinition<WorldZoneDef>)(object)worldZoneData).Definition != null)
							{
								zone = ((BalanceBaseObject)((ObjectLinkedToDefinition<WorldZoneDef>)(object)worldZoneData).Definition).id ?? "";
							}
						}
						catch
						{
						}
						AddStored(val2.Data.Inventory, dictionary, zone, 0);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogThrottled("Storage scan failed: " + ex.Message);
			}
			_stored = dictionary;
			_storedAt = Time.unscaledTime;
			return dictionary;
		}

		private static void AddStored(List<Item> items, Dictionary<string, Dictionary<string, int>> map, string zone, int depth)
		{
			if (items == null)
			{
				return;
			}
			foreach (Item item in items)
			{
				if (item == null || item.IsEmpty)
				{
					continue;
				}
				string text = null;
				try
				{
					text = ((((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition == null) ? null : ((BalanceBaseObject)((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition).id);
				}
				catch
				{
				}
				if (!string.IsNullOrEmpty(text) && item.Count > 0)
				{
					if (!map.TryGetValue(text, out var value))
					{
						value = (map[text] = new Dictionary<string, int>());
					}
					value[zone] = (value.TryGetValue(zone, out var value2) ? value2 : 0) + item.Count;
				}
				if (depth < 3 && item.IsBag)
				{
					try
					{
						AddStored(item.Inventory, map, zone, depth + 1);
					}
					catch
					{
					}
				}
			}
		}

		public static List<Need> ToNeeds(IEnumerable<NeedItemData> needs, WgoData wgo)
		{
			List<Need> list = new List<Need>();
			if (needs == null)
			{
				return list;
			}
			foreach (NeedItemData n in needs)
			{
				if (n == null)
				{
					continue;
				}
				bool flag = false;
				try
				{
					flag = n.IsEmpty;
				}
				catch
				{
				}
				if (flag || string.IsNullOrEmpty(n.id))
				{
					continue;
				}
				int num = 0;
				try
				{
					num = n.GetCount(wgo);
				}
				catch
				{
					try
					{
						num = ((n.count != null) ? n.count.EvaluateInt() : 0);
					}
					catch
					{
						num = 0;
					}
				}
				if (num > 0)
				{
					bool isGroup = false;
					try
					{
						isGroup = n.IsGroup;
					}
					catch
					{
					}
					Need need = list.FirstOrDefault((Need x) => x.Id == n.id);
					if (need != null)
					{
						need.Count += num;
						continue;
					}
					list.Add(new Need
					{
						Id = n.id,
						Count = num,
						IsGroup = isGroup
					});
				}
			}
			return list;
		}

		public static object WidgetData(object widget)
		{
			try
			{
				return Traverse.Create(widget).Field("data").GetValue();
			}
			catch
			{
				return null;
			}
		}

		public static TaskEntry TaskFromBuildWidget(UIBuildingWidget w)
		{
			object obj = WidgetData(w);
			UIBuildingWidgetData val = (UIBuildingWidgetData)((obj is UIBuildingWidgetData) ? obj : null);
			if (val == null || val.BuildData == null)
			{
				return null;
			}
			BuildingDef definition = val.BuildData.Definition;
			string wgoId = val.BuildData.WgoId;
			if (string.IsNullOrEmpty(wgoId) && definition != null)
			{
				wgoId = definition.wgoId;
			}
			if (string.IsNullOrEmpty(wgoId))
			{
				return null;
			}
			List<NeedItemData> list = null;
			try
			{
				list = val.GetCurrentNeedItems();
			}
			catch
			{
			}
			if ((list == null || list.Count == 0) && definition != null)
			{
				list = definition.needItems;
			}
			string text = null;
			string iconId = null;
			try
			{
				text = ((definition == null) ? null : definition.GetHeader());
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text))
			{
				text = L(wgoId) ?? wgoId;
			}
			try
			{
				iconId = ((definition == null) ? null : definition.BuildResultIcon);
			}
			catch
			{
			}
			TaskEntry taskEntry = new TaskEntry();
			taskEntry.Kind = "build";
			taskEntry.Id = wgoId;
			taskEntry.DefId = ((BalanceBaseObject)(definition?)).id;
			taskEntry.Name = text;
			taskEntry.IconId = iconId;
			taskEntry.Needs = ToNeeds(list, null);
			return taskEntry;
		}

		public static TaskEntry TaskFromTownBuildingWidget(object w)
		{
			object obj = WidgetData(w);
			UITownBuildingWidgetData val = (UITownBuildingWidgetData)((obj is UITownBuildingWidgetData) ? obj : null);
			if (val == null)
			{
				return null;
			}
			TownBuildingDef townBuildingDef = val.TownBuildingDef;
			if (townBuildingDef == null || string.IsNullOrEmpty(((BalanceBaseObject)townBuildingDef).id))
			{
				return null;
			}
			List<NeedItemData> list = null;
			try
			{
				List<UICraftItemCellData> craftItemCellsData = val.CraftItemCellsData;
				if (craftItemCellsData != null && craftItemCellsData.Count > 0)
				{
					list = new List<NeedItemData>();
					foreach (UICraftItemCellData item in craftItemCellsData)
					{
						if (item != null && item.currentItem != null)
						{
							list.Add(item.currentItem);
						}
					}
				}
			}
			catch
			{
			}
			if (list == null || list.Count == 0)
			{
				list = townBuildingDef.needItems;
			}
			string text = null;
			string text2 = null;
			try
			{
				text = townBuildingDef.GetHeader();
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text))
			{
				try
				{
					text = val.Name;
				}
				catch
				{
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				text = L(((BalanceBaseObject)townBuildingDef).id) ?? ((BalanceBaseObject)townBuildingDef).id;
			}
			try
			{
				text2 = townBuildingDef.BuildResultIcon;
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text2))
			{
				try
				{
					text2 = townBuildingDef.iconId;
				}
				catch
				{
				}
			}
			TaskEntry taskEntry = new TaskEntry();
			taskEntry.Kind = "town";
			taskEntry.Id = ((BalanceBaseObject)townBuildingDef).id;
			taskEntry.DefId = ((BalanceBaseObject)townBuildingDef).id;
			taskEntry.Name = text;
			taskEntry.IconId = text2;
			taskEntry.Needs = ToNeeds(list, null);
			return taskEntry;
		}

		public static TaskEntry TaskFromLinkedEntity(object widget)
		{
			LinkedEntityWidgetData val = null;
			try
			{
				object value = Traverse.Create(widget).Property("WidgetData", (object[])null).GetValue();
				val = (LinkedEntityWidgetData)((value is LinkedEntityWidgetData) ? value : null);
			}
			catch
			{
			}
			if (val == null)
			{
				object obj2 = WidgetData(widget);
				val = (LinkedEntityWidgetData)((obj2 is LinkedEntityWidgetData) ? obj2 : null);
			}
			if (val == null)
			{
				return null;
			}
			if (val.CraftDef != null)
			{
				return TaskFromCraft(val.CraftDef, null);
			}
			BuildingDef buildingDef = val.BuildingDef;
			if (buildingDef != null && !string.IsNullOrEmpty(buildingDef.wgoId))
			{
				string text = null;
				string iconId = null;
				try
				{
					text = buildingDef.GetHeader();
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text))
				{
					text = L(buildingDef.wgoId) ?? buildingDef.wgoId;
				}
				try
				{
					iconId = buildingDef.BuildResultIcon;
				}
				catch
				{
				}
				TaskEntry taskEntry = new TaskEntry();
				taskEntry.Kind = "build";
				taskEntry.Id = buildingDef.wgoId;
				taskEntry.DefId = ((BalanceBaseObject)buildingDef).id;
				taskEntry.Name = text;
				taskEntry.IconId = iconId;
				taskEntry.Needs = ToNeeds(buildingDef.needItems, null);
				return taskEntry;
			}
			TownBuildingDef townBuildingDef = val.TownBuildingDef;
			if (townBuildingDef != null && !string.IsNullOrEmpty(((BalanceBaseObject)townBuildingDef).id))
			{
				string text2 = null;
				string text3 = null;
				try
				{
					text2 = townBuildingDef.GetHeader();
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text2))
				{
					text2 = L(((BalanceBaseObject)townBuildingDef).id) ?? ((BalanceBaseObject)townBuildingDef).id;
				}
				try
				{
					text3 = townBuildingDef.BuildResultIcon;
				}
				catch
				{
				}
				if (string.IsNullOrEmpty(text3))
				{
					try
					{
						text3 = townBuildingDef.iconId;
					}
					catch
					{
					}
				}
				TaskEntry taskEntry = new TaskEntry();
				taskEntry.Kind = "town";
				taskEntry.Id = ((BalanceBaseObject)townBuildingDef).id;
				taskEntry.DefId = ((BalanceBaseObject)townBuildingDef).id;
				taskEntry.Name = text2;
				taskEntry.IconId = text3;
				taskEntry.Needs = ToNeeds(townBuildingDef.needItems, null);
				return taskEntry;
			}
			return null;
		}

		public static TaskEntry TaskFromCraft(CraftDef craft, WgoData wgo)
		{
			if (craft == null || string.IsNullOrEmpty(((BalanceBaseObject)craft).id))
			{
				return null;
			}
			string text = null;
			string text2 = null;
			try
			{
				ItemDef val = craft.TryGetResultingItemDef(true);
				if (val != null)
				{
					text = val.GetHeader();
				}
			}
			catch
			{
			}
			if (IsRaw(text, ((BalanceBaseObject)craft).id))
			{
				text = CraftName(((BalanceBaseObject)craft).id);
			}
			if (string.IsNullOrEmpty(text))
			{
				text = Pretty(((BalanceBaseObject)craft).id);
			}
			try
			{
				text2 = craft.GetCraftResultIcon(wgo);
			}
			catch
			{
			}
			if (string.IsNullOrEmpty(text2))
			{
				try
				{
					ItemDef val2 = craft.TryGetResultingItemDef(true);
					if (val2 != null)
					{
						text2 = val2.iconId;
					}
				}
				catch
				{
				}
			}
			TaskEntry taskEntry = new TaskEntry();
			taskEntry.Kind = "craft";
			taskEntry.Id = ((BalanceBaseObject)craft).id;
			taskEntry.Name = text;
			taskEntry.IconId = text2;
			taskEntry.Needs = ToNeeds(((CraftDefBase)craft).needItems, wgo);
			return taskEntry;
		}

		public static TaskEntry TaskFromPreviewCell(UICraftPreviewItemCell cell)
		{
			object obj = WidgetData(cell);
			UICraftPreviewItemCellData val = (UICraftPreviewItemCellData)((obj is UICraftPreviewItemCellData) ? obj : null);
			if (val == null)
			{
				return null;
			}
			try
			{
				if (val.IsUnknown || val.IsTab)
				{
					return null;
				}
			}
			catch
			{
			}
			return TaskFromCraft(val.CraftDef, val.WgoData);
		}

		public static TaskEntry TaskFromCraftWidget(object widget)
		{
			object obj = WidgetData(widget);
			UIBaseCraftWidgetData val = (UIBaseCraftWidgetData)((obj is UIBaseCraftWidgetData) ? obj : null);
			if (val == null)
			{
				return null;
			}
			TaskEntry taskEntry = TaskFromCraft(val.CraftDefinition, val.WgoData);
			if (taskEntry != null)
			{
				try
				{
					List<NeedItemData> currentNeedItems = val.GetCurrentNeedItems();
					if (currentNeedItems != null && currentNeedItems.Count > 0)
					{
						taskEntry.Needs = ToNeeds(currentNeedItems, val.WgoData);
					}
				}
				catch
				{
				}
			}
			return taskEntry;
		}

		public static TaskEntry TaskFromSelectionWindow(object window)
		{
			object obj = WidgetData(window);
			UIBaseCraftSelectionWindowData val = (UIBaseCraftSelectionWindowData)((obj is UIBaseCraftSelectionWindowData) ? obj : null);
			if (val == null)
			{
				return null;
			}
			TaskEntry taskEntry = TaskFromCraft(val.CraftDefinition, val.WgoData);
			if (taskEntry == null)
			{
				return null;
			}
			try
			{
				List<NeedItemData> currentNeedItems = val.CurrentNeedItems;
				if (currentNeedItems != null && currentNeedItems.Count > 0)
				{
					taskEntry.Needs = ToNeeds(currentNeedItems, val.WgoData);
				}
			}
			catch
			{
			}
			return taskEntry;
		}

		public static object OpenCraftSelectionWindow()
		{
			try
			{
				UIBaseCraftSelectionWindow[] array = Object.FindObjectsOfType<UIBaseCraftSelectionWindow>();
				foreach (UIBaseCraftSelectionWindow val in array)
				{
					if ((Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		public static List<string> RepairSources(string kind, string id, out string replacement)
		{
			replacement = null;
			if (string.IsNullOrEmpty(id))
			{
				return null;
			}
			try
			{
				if (kind == "town" && id.StartsWith("repair_"))
				{
					TownBuildingDef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<TownBuildingDef>(id);
					return (dataOrNull == null || dataOrNull.craftsIn == null || dataOrNull.craftsIn.Count <= 0) ? null : new List<string>(dataOrNull.craftsIn);
				}
				if (kind == "craft" && (id.Contains("repair") || id.Contains("blockage")))
				{
					CraftDef dataOrNull2 = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<CraftDef>(id);
					if (dataOrNull2 == null || ((CraftDefBase)dataOrNull2).craftsIn == null || ((CraftDefBase)dataOrNull2).craftsIn.Count == 0)
					{
						return null;
					}
					if (!string.IsNullOrEmpty(dataOrNull2.replaceWgoId) && dataOrNull2.replaceWgoId != "0")
					{
						replacement = dataOrNull2.replaceWgoId;
					}
					return new List<string>(((CraftDefBase)dataOrNull2).craftsIn);
				}
			}
			catch
			{
			}
			return null;
		}

		public static bool IsRepair(string kind, string id)
		{
			string replacement;
			return RepairSources(kind, id, out replacement) != null;
		}

		public static HashSet<string> WorldWgoIds()
		{
			HashSet<string> hashSet = new HashSet<string>();
			try
			{
				WorldData worldData = MainGame.Instance.GameSave.worldData;
				foreach (GameSceneData gameSceneData in worldData.gameSceneDataList)
				{
					if (gameSceneData == null || gameSceneData.wgoDataList == null)
					{
						continue;
					}
					foreach (WgoData wgoData in gameSceneData.wgoDataList)
					{
						if (wgoData != null && !string.IsNullOrEmpty(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id))
						{
							hashSet.Add(((ObjectLinkedToDefinition<WGODef>)(object)wgoData).id);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("World scan failed: " + ex.Message));
			}
			return hashSet;
		}

		public static object OpenQuestWindow()
		{
			try
			{
				UIQuestInfoWindow[] array = Object.FindObjectsOfType<UIQuestInfoWindow>();
				foreach (UIQuestInfoWindow val in array)
				{
					if ((Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled)
					{
						return val;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		public static TaskEntry TaskFromQuestWindow(object window)
		{
			object obj = WidgetData(window);
			UIQuestInfoWindowData val = (UIQuestInfoWindowData)((obj is UIQuestInfoWindowData) ? obj : null);
			return (val != null) ? TaskFromQuest(val.QuestData) : null;
		}

		public static TaskEntry TaskFromQuestTreeWidget(object widget)
		{
			object obj = WidgetData(widget);
			QuestTreeElementWidgetData val = (QuestTreeElementWidgetData)((obj is QuestTreeElementWidgetData) ? obj : null);
			return (val != null) ? TaskFromQuest(((TreeElementBaseWidgetData)val).questData) : null;
		}

		public static string QuestRefusal(QuestData qd)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Invalid comparison between Unknown and I4
			if (qd == null)
			{
				return "no quest";
			}
			QuestViewStatus viewStatus;
			try
			{
				viewStatus = qd.ViewStatus;
			}
			catch
			{
				return null;
			}
			if ((int)viewStatus == 0 || (int)viewStatus == 1)
			{
				return "not revealed yet";
			}
			if ((int)viewStatus == 4)
			{
				return "already completed";
			}
			return null;
		}

		public static TaskEntry TaskFromQuest(QuestData qd)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (qd == null || string.IsNullOrEmpty(((ObjectLinkedToDefinition<QuestDef>)(object)qd).id))
			{
				return null;
			}
			QuestDef val = null;
			try
			{
				val = ((ObjectLinkedToDefinition<QuestDef>)(object)qd).Definition;
			}
			catch
			{
			}
			if (val == null)
			{
				return null;
			}
			List<Need> list = new List<Need>();
			try
			{
				QuestFinishCheck finishCheck = val.finishCheck;
				if (finishCheck != null && finishCheck.phraseReqs != null)
				{
					foreach (QuestPhraseRequirement phraseReq in finishCheck.phraseReqs)
					{
						if (phraseReq == null || (int)phraseReq.entity != 0)
						{
							continue;
						}
						ItemCount ic = phraseReq.itemCount;
						if (ic != null && !string.IsNullOrEmpty(ic.itemId) && ic.count > 0)
						{
							Need need = list.FirstOrDefault((Need n) => n.Id == ic.itemId);
							if (need != null)
							{
								need.Count += ic.count;
								continue;
							}
							list.Add(new Need
							{
								Id = ic.itemId,
								Count = ic.count
							});
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Quest requirements (" + ((ObjectLinkedToDefinition<QuestDef>)(object)qd).id + "): " + ex.Message));
			}
			string name = QuestName(((ObjectLinkedToDefinition<QuestDef>)(object)qd).id);
			string text = null;
			try
			{
				text = ((!string.IsNullOrEmpty(val.iconId)) ? val.iconId : "default_quest_icon");
			}
			catch
			{
				text = "default_quest_icon";
			}
			TaskEntry taskEntry = new TaskEntry();
			taskEntry.Kind = "quest";
			taskEntry.Id = ((ObjectLinkedToDefinition<QuestDef>)(object)qd).id;
			taskEntry.Name = name;
			taskEntry.IconId = text;
			taskEntry.Needs = list;
			return taskEntry;
		}

		private static string QuestName(string questId)
		{
			string text = L(questId);
			if (!IsRaw(text, questId))
			{
				return text;
			}
			string text2 = L("quest_open_" + questId);
			if (!IsRaw(text2, questId))
			{
				return text2;
			}
			return Pretty(questId);
		}

		public static TaskEntry TaskFromItem(ItemDef def)
		{
			if (def == null)
			{
				return null;
			}
			string text = null;
			try
			{
				text = def.GetHeader();
			}
			catch
			{
			}
			TaskEntry taskEntry = new TaskEntry();
			taskEntry.Kind = "item";
			taskEntry.Id = ((BalanceBaseObject)def).id;
			taskEntry.Name = ((!string.IsNullOrEmpty(text)) ? text : ((BalanceBaseObject)def).id);
			taskEntry.IconId = def.iconId;
			taskEntry.Needs = new List<Need>
			{
				new Need
				{
					Id = ((BalanceBaseObject)def).id,
					Count = 1
				}
			};
			return taskEntry;
		}

		public static string OrderTaskName(string id)
		{
			int num = id?.IndexOf('@') ?? (-1);
			if (num <= 0)
			{
				return ItemName(id, isGroup: false);
			}
			string text = id.Substring(0, num);
			string id2 = id.Substring(num + 1);
			string text2 = null;
			try
			{
				foreach (OrderInfo item in Orders.Current())
				{
					if (item.VendorId == text)
					{
						text2 = item.VendorName;
						break;
					}
				}
			}
			catch
			{
			}
			return ItemName(id2, isGroup: false) + " · " + (text2 ?? L(text) ?? text);
		}

		public static string TaskName(TaskEntry t)
		{
			string text = null;
			try
			{
				text = LLBase.CurrentLang;
			}
			catch
			{
			}
			if (text != _nameCacheLang)
			{
				_nameCache.Clear();
				_nameCacheLang = text;
			}
			if (_nameCache.TryGetValue(t.Key, out var value))
			{
				return value;
			}
			value = null;
			try
			{
				if (!(t.Kind == "build"))
				{
					value = ((t.Kind == "craft") ? CraftName(t.Id) : ((t.Kind == "quest") ? QuestName(t.Id) : ((!(t.Kind == "order")) ? ItemName(t.Id, isGroup: false) : OrderTaskName(t.Id))));
				}
				else
				{
					foreach (BuildingDef buildingDef in GameBalance.Me.buildingDefs)
					{
						if (buildingDef != null && buildingDef.wgoId == t.Id)
						{
							value = Clean(buildingDef.GetHeader(), ((BalanceBaseObject)buildingDef).id);
							break;
						}
					}
					if (string.IsNullOrEmpty(value))
					{
						value = L(t.Id);
					}
				}
			}
			catch
			{
			}
			if (IsRaw(value, t.Id))
			{
				value = null;
			}
			if (string.IsNullOrEmpty(value) && !IsRaw(t.Name, t.Id))
			{
				value = t.Name;
			}
			if (string.IsNullOrEmpty(value))
			{
				value = Pretty(t.Id);
			}
			_nameCache[t.Key] = value;
			return value;
		}

		private static bool IsRaw(string name, string id)
		{
			if (string.IsNullOrEmpty(name))
			{
				return true;
			}
			if (name == id)
			{
				return true;
			}
			return name.IndexOf(' ') < 0 && (name.IndexOf('_') >= 0 || name.IndexOf(':') >= 0);
		}

		private static string Clean(string name, string key)
		{
			return (!IsRaw(name, key)) ? name : null;
		}

		private static string Pretty(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return "";
			}
			string text = id.Replace(':', ' ').Replace('_', ' ').Trim();
			if (text.Length == 0)
			{
				return id;
			}
			return char.ToUpper(text[0]) + text.Substring(1);
		}

		private static string TrimState(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return id;
			}
			string[] array = new string[3] { "_broken", "_empty", "_place" };
			foreach (string text in array)
			{
				if (id.EndsWith(text))
				{
					return id.Substring(0, id.Length - text.Length);
				}
			}
			int num = id.LastIndexOf('_');
			if (num > 0)
			{
				bool flag = num + 1 < id.Length;
				for (int j = num + 1; j < id.Length; j++)
				{
					if (!flag)
					{
						break;
					}
					if (!char.IsDigit(id[j]))
					{
						flag = false;
					}
				}
				if (flag)
				{
					return id.Substring(0, num);
				}
			}
			return id;
		}

		private static string CraftName(string craftId)
		{
			string text = L(craftId);
			if (!IsRaw(text, craftId))
			{
				return text;
			}
			int num = craftId.IndexOf(':');
			if (num >= 0)
			{
				string text2 = L(craftId.Substring(num + 1));
				if (!IsRaw(text2, craftId))
				{
					return text2;
				}
			}
			CraftDef val = null;
			try
			{
				val = GameBalance.GetCraftDef(craftId);
			}
			catch
			{
			}
			if (val == null)
			{
				return null;
			}
			try
			{
				ItemDef val2 = val.TryGetResultingItemDef(true);
				if (val2 != null)
				{
					string text3 = Clean(val2.GetHeader(), ((BalanceBaseObject)val2).id);
					if (text3 != null)
					{
						return text3;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (!((CraftDefBase)val).isFuelCraft && ((CraftDefBase)val).needItems != null)
				{
					string text4 = null;
					bool flag = true;
					foreach (NeedItemData needItem in ((CraftDefBase)val).needItems)
					{
						if (needItem != null && !string.IsNullOrEmpty(needItem.id))
						{
							if (text4 == null)
							{
								text4 = needItem.id;
							}
							else if (text4 != needItem.id)
							{
								flag = false;
								break;
							}
						}
					}
					if (flag && text4 != null)
					{
						string text5 = ItemName(text4, isGroup: false);
						if (!IsRaw(text5, text4))
						{
							return text5;
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				if (((CraftDefBase)val).craftsIn != null)
				{
					foreach (string item in ((CraftDefBase)val).craftsIn)
					{
						if (!string.IsNullOrEmpty(item))
						{
							string text6 = L(item);
							if (!IsRaw(text6, item))
							{
								return text6;
							}
							string text7 = TrimState(item);
							text6 = L(text7);
							if (!IsRaw(text6, text7))
							{
								return text6;
							}
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				KeyValuePair<string, string>[] families = Families;
				for (int i = 0; i < families.Length; i++)
				{
					KeyValuePair<string, string> keyValuePair = families[i];
					bool flag2 = craftId.Contains(keyValuePair.Key);
					if (!flag2 && ((CraftDefBase)val).craftsIn != null)
					{
						foreach (string item2 in ((CraftDefBase)val).craftsIn)
						{
							if (!string.IsNullOrEmpty(item2) && item2.Contains(keyValuePair.Key))
							{
								flag2 = true;
								break;
							}
						}
					}
					if (flag2)
					{
						string text8 = L(keyValuePair.Value);
						if (!IsRaw(text8, keyValuePair.Value))
						{
							return text8;
						}
					}
				}
			}
			catch
			{
			}
			return null;
		}

		public static void ResetCaches()
		{
			_groupCache.Clear();
			_nameCache.Clear();
		}
	}
	internal static class Hotkey
	{
		internal static bool Down(ConfigEntry<KeyboardShortcut> entry, params ConfigEntry<KeyboardShortcut>[] rivals)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (entry == null)
				{
					return false;
				}
				KeyboardShortcut value = entry.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey) || !Held(value))
				{
					return false;
				}
				int num = ((KeyboardShortcut)(ref value)).Modifiers.Count();
				if (rivals != null)
				{
					foreach (ConfigEntry<KeyboardShortcut> val in rivals)
					{
						if (val != null && !object.ReferenceEquals(val, entry))
						{
							KeyboardShortcut value2 = val.Value;
							if (((KeyboardShortcut)(ref value2)).MainKey == ((KeyboardShortcut)(ref value)).MainKey && ((KeyboardShortcut)(ref value2)).Modifiers.Count() > num && Held(value2))
							{
								return false;
							}
						}
					}
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool Held(KeyboardShortcut s)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref s)).Modifiers)
			{
				if (!Input.GetKey(modifier))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class L10n
	{
		private static readonly string[] Langs = new string[11]
		{
			"en", "ru", "de", "fr", "es", "pt", "pl", "tr", "ja", "zh",
			"ko"
		};

		private static readonly Dictionary<string, string[]> Table = new Dictionary<string, string[]>
		{
			{
				"Tasks",
				new string[11]
				{
					"Tasks", "Задачи", "Aufgaben", "Tâches", "Tareas", "Tarefas", "Zadania", "Görevler", "タスク", "任务",
					"작업"
				}
			},
			{
				"Bring",
				new string[11]
				{
					"Bring", "Взять", "Mitnehmen", "À apporter", "Llevar", "Levar", "Zabrać", "Getir", "持参", "携带",
					"챙길 것"
				}
			},
			{
				"Title",
				new string[11]
				{
					"Shopping list", "Список покупок", "Einkaufsliste", "Liste de courses", "Lista de compras", "Lista de compras", "Lista zakupów", "Alışveriş listesi", "買い物リスト", "购物清单",
					"쇼핑 목록"
				}
			},
			{
				"Total",
				new string[11]
				{
					"Total", "Итого", "Gesamt", "Total", "Total", "Total", "Razem", "Toplam", "合計", "合计",
					"합계"
				}
			},
			{
				"ByTask",
				new string[11]
				{
					"By task", "По задачам", "Nach Aufgabe", "Par tâche", "Por tarea", "Por tarefa", "Według zadań", "Göreve göre", "タスク別", "按任务",
					"작업별"
				}
			},
			{
				"Empty",
				new string[11]
				{
					"The list is empty.\nCtrl+RMB a recipe in a craft\nor build window\nor a quest in the quest tree", "Список пуст.\nCtrl+ПКМ по рецепту в окне\nкрафта или стройки\nили по заданию в дереве заданий", "Die Liste ist leer.\nStrg+Rechtsklick auf ein Rezept\nim Herstellungs- oder Baufenster\noder auf eine Quest im Questbaum", "La liste est vide.\nCtrl+clic droit sur une recette\ndans une fenêtre de fabrication ou de construction\nou sur une quête dans l'arbre des quêtes", "La lista está vacía.\nCtrl+clic derecho en una receta\nen la ventana de fabricación o construcción\no en una misión del árbol de misiones", "A lista está vazia.\nCtrl+clique direito em uma receita\nna janela de fabricação ou construção\nou em uma missão na árvore de missões", "Lista jest pusta.\nCtrl+PPM na przepisie w oknie\nwytwarzania lub budowy\nlub na zadaniu w drzewie zadań", "Liste boş.\nÜretim veya inşa penceresinde\nbir tarife Ctrl+Sağ tık\nveya görev ağacındaki bir göreve", "リストは空です。\n製作・建設画面でレシピを\nCtrl+右クリック\nクエストツリーのクエストでも可", "清单为空。\n在制作或建造窗口中\nCtrl+右键点击配方\n或任务树中的任务",
					"목록이 비어 있습니다.\n제작 또는 건설 창에서\n레시피를 Ctrl+우클릭\n또는 퀘스트 트리의 퀘스트를"
				}
			},
			{
				"AllDone",
				new string[11]
				{
					"All collected", "Всё собрано", "Alles gesammelt", "Tout est réuni", "Todo reunido", "Tudo reunido", "Wszystko zebrane", "Hepsi toplandı", "すべて揃いました", "已全部备齐",
					"모두 모음"
				}
			},
			{
				"More",
				new string[11]
				{
					"…and {0}", "…ещё {0}", "…und {0}", "…et {0}", "…y {0}", "…e {0}", "…i {0}", "…ve {0}", "…他{0}", "…还有{0}",
					"…외 {0}"
				}
			},
			{
				"Added",
				new string[11]
				{
					"Added: ", "В список: ", "Hinzugefügt: ", "Ajouté : ", "Añadido: ", "Adicionado: ", "Dodano: ", "Eklendi: ", "追加: ", "已添加:",
					"추가됨: "
				}
			},
			{
				"NothingToBring",
				new string[11]
				{
					"nothing to bring", "брать с собой ничего не нужно", "nichts mitzunehmen", "rien à apporter", "nada que llevar", "nada para levar", "nic do zabrania", "getirilecek bir şey yok", "持参するものはありません", "无需携带",
					"챙길 것 없음"
				}
			},
			{
				"QuestNoItems",
				new string[11]
				{
					"this quest asks for no items", "для этого задания ничего носить не нужно", "diese Quest verlangt keine Gegenstände", "cette quête ne demande aucun objet", "esta misión no pide objetos", "esta missão não pede itens", "to zadanie nie wymaga przedmiotów", "bu görev eşya istemiyor", "このクエストにアイテムは不要です", "该任务不需要物品",
					"이 퀘스트는 아이템이 필요 없습니다"
				}
			},
			{
				"PadSelect",
				new string[11]
				{
					"select", "выбрать", "wählen", "choisir", "elegir", "escolher", "wybierz", "seç", "決定", "选择",
					"선택"
				}
			},
			{
				"PadClose",
				new string[11]
				{
					"close", "закрыть", "schließen", "fermer", "cerrar", "fechar", "zamknij", "kapat", "閉じる", "关闭",
					"닫기"
				}
			},
			{
				"PadTabs",
				new string[11]
				{
					"tabs", "вкладки", "Tabs", "onglets", "pestañas", "abas", "karty", "sekmeler", "タブ", "标签",
					"탭"
				}
			},
			{
				"PadAddHint",
				new string[11]
				{
					"add to the list", "добавить в список", "zur Liste", "ajouter à la liste", "añadir a la lista", "adicionar à lista", "dodaj do listy", "listeye ekle", "リストに追加", "加入清单",
					"목록에 추가"
				}
			},
			{
				"OrdersAdded",
				new string[11]
				{
					"Town orders added: {0}", "Добавлено заказов: {0}", "Stadtaufträge hinzugefügt: {0}", "Commandes ajoutées : {0}", "Encargos añadidos: {0}", "Encomendas adicionadas: {0}", "Dodano zamówień: {0}", "Eklenen siparişler: {0}", "注文を追加:{0}", "已添加订单:{0}",
					"추가된 주문: {0}"
				}
			},
			{
				"OrdersAlready",
				new string[11]
				{
					"All town orders are on the list already", "Все заказы уже в списке", "Alle Stadtaufträge stehen schon auf der Liste", "Toutes les commandes sont déjà dans la liste", "Todos los encargos ya están en la lista", "Todas as encomendas já estão na lista", "Wszystkie zamówienia są już na liście", "Tüm siparişler zaten listede", "注文はすべてリストにあります", "所有订单都已在清单中",
					"모든 주문이 이미 목록에 있습니다"
				}
			},
			{
				"OrdersNone",
				new string[11]
				{
					"No open town orders", "Открытых заказов нет", "Keine offenen Stadtaufträge", "Aucune commande en cours", "No hay encargos abiertos", "Nenhuma encomenda aberta", "Brak otwartych zamówień", "Açık sipariş yok", "受けている注文はありません", "没有进行中的订单",
					"진행 중인 주문이 없습니다"
				}
			},
			{
				"ClearAll",
				new string[11]
				{
					"Clear all", "Очистить всё", "Alles löschen", "Tout effacer", "Borrar todo", "Limpar tudo", "Wyczyść wszystko", "Tümünü temizle", "すべて消去", "全部清除",
					"모두 지우기"
				}
			},
			{
				"ListCleared",
				new string[11]
				{
					"The list is empty", "Список пуст", "Die Liste ist leer", "La liste est vide", "La lista está vacía", "A lista está vazia", "Lista jest pusta", "Liste boş", "リストは空です", "清单为空",
					"목록이 비었습니다"
				}
			},
			{
				"ClearConfirm",
				new string[11]
				{
					"Clear {0}? Press again", "Удалить {0}? Нажмите ещё раз", "{0} löschen? Nochmal drücken", "Effacer {0} ? Appuyez encore", "¿Borrar {0}? Pulsa otra vez", "Limpar {0}? Pressione de novo", "Usunąć {0}? Naciśnij ponownie", "{0} silinsin mi? Tekrar bas", "{0}件消去?もう一度押す", "清除{0}项?再按一次",
					"{0}개 지우기? 한 번 더 누르세요"
				}
			},
			{
				"Orders",
				new string[11]
				{
					"Orders", "Заказы", "Aufträge", "Commandes", "Pedidos", "Pedidos", "Zlecenia", "Siparişler", "注文", "订单",
					"주문"
				}
			},
			{
				"NoOrders",
				new string[11]
				{
					"No orders taken.\nThe town hands them out once a week.", "Заказов нет.\nГород выдаёт их раз в неделю.", "Keine Aufträge.\nDie Stadt vergibt sie einmal pro Woche.", "Aucune commande.\nLa ville en donne une fois par semaine.", "No hay pedidos.\nLa ciudad los da una vez por semana.", "Nenhum pedido.\nA cidade os dá uma vez por semana.", "Brak zleceń.\nMiasto wydaje je raz w tygodniu.", "Sipariş yok.\nKasaba haftada bir verir.", "注文はありません。\n町は週に一度出します。", "没有订单。\n镇上每周发放一次。",
					"주문이 없습니다.\n마을은 주 1회 내놓습니다."
				}
			},
			{
				"StorageHint",
				new string[11]
				{
					"+N Home: that many more lie in your chests there", "+N Дом — столько ещё лежит в ваших сундуках там", "+N Haus: so viele liegen dort noch in deinen Truhen", "+N Maison : autant dans vos coffres là-bas", "+N Casa: esa cantidad más hay en tus cofres allí", "+N Casa: essa quantidade a mais nos seus baús lá", "+N Dom: tyle jeszcze leży tam w twoich skrzyniach", "+N Ev: oradaki sandıklarında bu kadar daha var", "+N 家: そこのチェストにある数", "+N 家:那里的箱子里还有这么多",
					"+N 집: 그곳 상자에 더 있는 수량"
				}
			},
			{
				"Elsewhere",
				new string[11]
				{
					"elsewhere", "в другом месте", "anderswo", "ailleurs", "en otro sitio", "em outro lugar", "gdzie indziej", "başka yerde", "他の場所", "其他地方",
					"다른 곳"
				}
			},
			{
				"OrdersHint",
				new string[11]
				{
					"Goods go on the town pallets in the Warehouse", "Товар кладётся на паллеты городского склада", "Waren auf die Stadtpaletten im Lager legen", "Les marchandises vont sur les palettes de l'entrepôt", "La mercancía va en los palés del almacén", "A mercadoria vai nos paletes do armazém", "Towar kładzie się na paletach w magazynie", "Mallar deponun paletlerine konur", "品物は倉庫のパレットに置きます", "货物放在仓库的托盘上",
					"물건은 창고 팔레트에 둡니다"
				}
			},
			{
				"OrdersToday",
				new string[11]
				{
					"New orders can be taken today {0}", "Сегодня можно взять новые заказы {0}", "Heute gibt es neue Aufträge {0}", "De nouvelles commandes aujourd'hui {0}", "Hoy se pueden tomar nuevos pedidos {0}", "Hoje dá para pegar novos pedidos {0}", "Dziś można wziąć nowe zlecenia {0}", "Bugün yeni sipariş alınabilir {0}", "今日は新しい注文を受けられます {0}", "今天可以接新订单 {0}",
					"오늘 새 주문을 받을 수 있습니다 {0}"
				}
			},
			{
				"OrdersInDays",
				new string[11]
				{
					"New orders in {0} d. {1}", "Новые заказы через {0} дн. {1}", "Neue Aufträge in {0} T. {1}", "Nouvelles commandes dans {0} j. {1}", "Nuevos pedidos en {0} d. {1}", "Novos pedidos em {0} d. {1}", "Nowe zlecenia za {0} dn. {1}", "Yeni siparişler {0} gün sonra {1}", "新しい注文は{0}日後 {1}", "{0} 天后有新订单 {1}",
					"새 주문까지 {0}일 {1}"
				}
			},
			{
				"OrderUrgent",
				new string[11]
				{
					"urgent: all at once", "срочный: всё сразу", "eilig: alles auf einmal", "urgent : tout d'un coup", "urgente: todo de una vez", "urgente: tudo de uma vez", "pilne: wszystko naraz", "acil: hepsi bir seferde", "緊急: 一度に全部", "紧急:一次凑齐",
					"긴급: 한 번에 전부"
				}
			},
			{
				"OrderReady",
				new string[11]
				{
					"ready — collect", "готов — забрать", "fertig – abholen", "prête — à récupérer", "listo — recoger", "pronto — recolher", "gotowe — odbierz", "hazır — teslim al", "完了 — 受け取る", "已完成 — 领取",
					"완료 — 수령"
				}
			},
			{
				"OrderOnPallets",
				new string[11]
				{
					"all there, counted tonight", "всё на месте, зачтут ночью", "alles da, wird heute Nacht gezählt", "tout y est, compté cette nuit", "todo listo, se cuenta esta noche", "tudo lá, contado esta noite", "wszystko jest, liczone tej nocy", "hepsi hazır, bu gece sayılır", "揃った、今夜カウント", "已齐,今晚结算",
					"모두 준비됨, 오늘 밤 집계"
				}
			},
			{
				"OrderCounted",
				new string[11]
				{
					"{0} counted in", "зачтено {0}", "{0} angerechnet", "{0} comptés", "{0} contados", "{0} contados", "zaliczono {0}", "{0} sayıldı", "{0}個 計上済み", "已计入 {0}",
					"{0} 반영됨"
				}
			}
		};

		private static string _lang;

		private static int _idx;

		public static int Index
		{
			get
			{
				string text = ExtLoc.Lang();
				if (text != _lang)
				{
					_lang = text;
					_idx = 0;
					for (int i = 0; i < Langs.Length; i++)
					{
						if (text.StartsWith(Langs[i]))
						{
							_idx = i;
							break;
						}
					}
				}
				return _idx;
			}
		}

		public static string T(string key)
		{
			if (ExtLoc.TryGet(key, out var text))
			{
				return text;
			}
			if (!Table.TryGetValue(key, out var value))
			{
				return key;
			}
			string text2 = value[Index];
			return (!string.IsNullOrEmpty(text2)) ? text2 : value[0];
		}

		public static string F(string key, object arg)
		{
			return string.Format(T(key), arg);
		}
	}
	public sealed class Need
	{
		public string Id;

		public int Count;

		public bool IsGroup;
	}
	public sealed class TaskEntry
	{
		public string Kind;

		public string Id;

		public string Name;

		public string IconId;

		public string DefId;

		public int Count;

		public List<Need> Needs = new List<Need>();

		[JsonIgnore]
		public string Key => Kind + ":" + Id;
	}
	public sealed class BringLine
	{
		public string Id;

		public bool IsGroup;

		public int Need;

		public int Have;

		public bool Done => Have >= Need;
	}
	public sealed class ShoppingList
	{
		public int Version = 1;

		public List<TaskEntry> Tasks = new List<TaskEntry>();

		[JsonIgnore]
		public string FilePath;

		[JsonIgnore]
		public int Revision;

		public TaskEntry Find(string kind, string id)
		{
			return Tasks.FirstOrDefault((TaskEntry t) => t.Kind == kind && t.Id == id);
		}

		public TaskEntry Add(TaskEntry template, int count)
		{
			TaskEntry taskEntry = Find(template.Kind, template.Id);
			if (taskEntry != null)
			{
				taskEntry.Count += count;
				if (template.Needs != null && template.Needs.Count > 0)
				{
					taskEntry.Needs = template.Needs;
				}
				Changed();
				return taskEntry;
			}
			template.Count = count;
			Tasks.Add(template);
			Changed();
			return template;
		}

		public void SetCount(TaskEntry t, int count)
		{
			if (count <= 0)
			{
				Tasks.Remove(t);
			}
			else
			{
				t.Count = count;
			}
			Changed();
		}

		public bool Consume(string kind, string id, int count)
		{
			TaskEntry taskEntry = Find(kind, id);
			if (taskEntry == null)
			{
				return false;
			}
			SetCount(taskEntry, taskEntry.Count - Math.Max(1, count));
			return true;
		}

		public bool ConsumeBuild(string wgoId, string defId, int count)
		{
			TaskEntry taskEntry = Tasks.FirstOrDefault((TaskEntry x) => x.Kind == "build" && Same(x, wgoId, defId));
			if (taskEntry == null)
			{
				return false;
			}
			SetCount(taskEntry, taskEntry.Count - Math.Max(1, count));
			return true;
		}

		private static bool Same(TaskEntry t, string wgoId, string defId)
		{
			return Matches(t.Id, wgoId) || Matches(t.Id, defId) || Matches(t.DefId, defId) || Matches(t.DefId, wgoId);
		}

		private static bool Matches(string a, string b)
		{
			if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b))
			{
				return false;
			}
			return Trim(a) == Trim(b);
		}

		private static string Trim(string id)
		{
			string[] array = new string[4] { "_place", "_pre", "_p", "_r" };
			foreach (string text in array)
			{
				if (id.EndsWith(text))
				{
					return id.Substring(0, id.Length - text.Length);
				}
			}
			return id;
		}

		public void Remove(TaskEntry t)
		{
			Tasks.Remove(t);
			Changed();
		}

		public void Clear()
		{
			if (Tasks.Count != 0)
			{
				Tasks.Clear();
				Changed();
			}
		}

		public void MoveUp(TaskEntry t, bool toTop = false)
		{
			int num = Tasks.IndexOf(t);
			if (num > 0)
			{
				Tasks.RemoveAt(num);
				Tasks.Insert((!toTop) ? (num - 1) : 0, t);
				Changed();
			}
		}

		public List<BringLine> Aggregate()
		{
			List<string> list = new List<string>();
			Dictionary<string, BringLine> map = new Dictionary<string, BringLine>();
			foreach (TaskEntry task in Tasks)
			{
				foreach (Need need in task.Needs)
				{
					if (!string.IsNullOrEmpty(need.Id) && need.Count > 0)
					{
						if (!map.TryGetValue(need.Id, out var value))
						{
							BringLine bringLine = new BringLine();
							bringLine.Id = need.Id;
							bringLine.IsGroup = need.IsGroup;
							value = bringLine;
							map.Add(need.Id, value);
							list.Add(need.Id);
						}
						value.Need += need.Count * task.Count;
					}
				}
			}
			return list.Select((string id) => map[id]).ToList();
		}

		private void Changed()
		{
			Revision++;
			Save();
		}

		public void Save()
		{
			if (string.IsNullOrEmpty(FilePath))
			{
				return;
			}
			string text = null;
			Exception ex = null;
			string contents;
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
				contents = JsonConvert.SerializeObject((object)this, (Formatting)1);
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogWarning((object)("Save failed: " + ex2.Message));
				return;
			}
			foreach (string item in SideFiles(FilePath))
			{
				try
				{
					File.WriteAllText(item, contents);
					text = item;
				}
				catch (Exception ex3)
				{
					ex = ex3;
					continue;
				}
				break;
			}
			if (text == null)
			{
				Plugin.Log.LogWarning((object)("Save failed: every side file is busy (" + ((ex == null) ? "?" : ex.Message) + ")"));
				return;
			}
			Exception ex4 = null;
			for (int i = 0; i < 8; i++)
			{
				try
				{
					if (!File.Exists(FilePath))
					{
						File.Move(text, FilePath);
					}
					else
					{
						try
						{
							File.Copy(FilePath, FilePath + ".bak", overwrite: true);
						}
						catch
						{
						}
						File.Copy(text, FilePath, overwrite: true);
					}
					foreach (string item2 in SideFiles(FilePath))
					{
						try
						{
							if (File.Exists(item2))
							{
								File.Delete(item2);
							}
						}
						catch
						{
						}
					}
					return;
				}
				catch (Exception ex5)
				{
					ex4 = ex5;
					Thread.Sleep(25);
				}
			}
			Plugin.Log.LogWarning((object)("Save: the file is busy, the list waits in " + text + " (" + ((ex4 == null) ? "?" : ex4.Message) + ")"));
		}

		internal static IEnumerable<string> SideFiles(string path)
		{
			yield return path + ".tmp";
			for (int i = 1; i < 5; i++)
			{
				yield return path + "." + i + ".tmp";
			}
		}

		public static ShoppingList Load(string path)
		{
			ShoppingList shoppingList = null;
			List<string> list = new List<string>();
			list.Add(path);
			list.Add(path + ".bak");
			List<string> list2 = list.Concat(SideFiles(path)).Where(File.Exists).OrderByDescending(delegate(string f)
			{
				try
				{
					return File.GetLastWriteTimeUtc(f);
				}
				catch
				{
					return DateTime.MinValue;
				}
			})
				.ToList();
			foreach (string item in list2)
			{
				try
				{
					shoppingList = JsonConvert.DeserializeObject<ShoppingList>(File.ReadAllText(item));
					if (shoppingList != null)
					{
						if (item != path)
						{
							Plugin.Log.LogWarning((object)("Load: the list came from " + item + " (the main file was missing, older or unreadable)"));
						}
						break;
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Load failed (" + item + "): " + ex.Message));
				}
			}
			if (shoppingList == null)
			{
				shoppingList = new ShoppingList();
			}
			if (shoppingList.Tasks == null)
			{
				shoppingList.Tasks = new List<TaskEntry>();
			}
			shoppingList.Tasks.RemoveAll((TaskEntry t) => t == null || t.Count <= 0);
			foreach (TaskEntry task in shoppingList.Tasks)
			{
				if (task.Needs == null)
				{
					task.Needs = new List<Need>();
				}
			}
			shoppingList.FilePath = path;
			return shoppingList;
		}
	}
	internal sealed class OrderInfo
	{
		public int Slot;

		public string VendorId;

		public string VendorName;

		public Sprite Portrait;

		public string ItemId;

		public int Need;

		public int Delivered;

		public int OnPallets;

		public bool Urgent;

		public bool Finished;

		public int Reward;

		public int Progress => Finished ? Need : ((!Urgent) ? (Delivered + OnPallets) : OnPallets);

		public bool Done => Finished || Progress >= Need;

		public bool WaitingForCount => !Finished && Progress >= Need;

		public int Left => Math.Max(0, Need - Progress);
	}
	internal static class Orders
	{
		public const string OrderDay = "day_pride";

		private const string WarehouseZone = "warehouse";

		private const string CellarZone = "warehouse_cellar";

		public const int WeekLength = 6;

		public static string[] DayIds()
		{
			try
			{
				return ConstDefs.AllDays ?? new string[0];
			}
			catch
			{
				return new string[0];
			}
		}

		public static int DayNumberOf(string dayId)
		{
			if (string.IsNullOrEmpty(dayId))
			{
				return -1;
			}
			try
			{
				ConstDef val = ConstDef.Get(dayId);
				if (val == null)
				{
					return -1;
				}
				int intValue = val.IntValue;
				return (intValue < 1 || intValue > 6) ? (-1) : intValue;
			}
			catch
			{
				return -1;
			}
		}

		public static int Today()
		{
			try
			{
				EnvironmentData val = Env();
				return (val != null) ? val.CurrentDayNumber : (-1);
			}
			catch
			{
				return -1;
			}
		}

		public static string TodayId()
		{
			int num = Today();
			if (num < 0)
			{
				return null;
			}
			string[] array = DayIds();
			foreach (string text in array)
			{
				if (DayNumberOf(text) == num)
				{
					return text;
				}
			}
			return null;
		}

		public static List<string> Week()
		{
			List<string> list = new List<string>();
			string[] array = new string[7];
			string[] array2 = DayIds();
			foreach (string text in array2)
			{
				int num = DayNumberOf(text);
				if (num >= 1 && num <= 6)
				{
					array[num] = text;
				}
			}
			for (int j = 1; j <= 6; j++)
			{
				if (array[j] != null)
				{
					list.Add(array[j]);
				}
			}
			return list;
		}

		public static int DaysUntil(string dayId)
		{
			int num = Today();
			int num2 = DayNumberOf(dayId);
			if (num < 0 || num2 < 0)
			{
				return -1;
			}
			return ((num2 - num) % 6 + 6) % 6;
		}

		public static bool OrderDayToday()
		{
			return DaysUntil("day_pride") == 0;
		}

		public static string DayIcon(string dayId)
		{
			if (string.IsNullOrEmpty(dayId))
			{
				return "";
			}
			try
			{
				return UIExtensions.FontIcon(dayId);
			}
			catch
			{
				return "";
			}
		}

		private static EnvironmentData Env()
		{
			MainGame instance = MainGame.Instance;
			return ((!((Object)(object)instance != (Object)null)) ? null : instance.GameSave)?.environmentData;
		}

		public static List<OrderInfo> Current()
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Invalid comparison between Unknown and I4
			List<OrderInfo> list = new List<OrderInfo>();
			try
			{
				MainGame instance = MainGame.Instance;
				VendorSystem val = ((!((Object)(object)instance != (Object)null)) ? null : instance.GameSave)?.vendorSystem;
				if (val == null)
				{
					return list;
				}
				List<(VendorOrderData, Vendor)> currentOrders = val.GetCurrentOrders();
				if (currentOrders == null)
				{
					return list;
				}
				for (int i = 0; i < currentOrders.Count; i++)
				{
					VendorOrderData item = currentOrders[i].Item1;
					Vendor item2 = currentOrders[i].Item2;
					if (item == null)
					{
						continue;
					}
					VendorOrderDef val2 = null;
					try
					{
						val2 = ((ObjectLinkedToDefinition<VendorOrderDef>)(object)item).Definition;
					}
					catch
					{
					}
					if (val2 != null && !string.IsNullOrEmpty(val2.itemId))
					{
						OrderInfo orderInfo = new OrderInfo();
						orderInfo.Slot = i;
						orderInfo.ItemId = val2.itemId;
						orderInfo.Need = val2.count;
						orderInfo.Urgent = val2.isUrgent;
						orderInfo.Finished = (int)item.State == 1;
						orderInfo.Delivered = Delivered(item);
						orderInfo.OnPallets = OnPallets(val2.itemId);
						orderInfo.Reward = Reward(val2);
						OrderInfo orderInfo2 = orderInfo;
						if (item2 != null)
						{
							orderInfo2.VendorId = ((ObjectLinkedToDefinition<VendorDef>)(object)item2).id;
							orderInfo2.VendorName = VendorName(item2);
							orderInfo2.Portrait = Portrait(item2);
						}
						list.Add(orderInfo2);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Orders: " + ex.Message));
			}
			return list;
		}

		private static int Delivered(VendorOrderData data)
		{
			try
			{
				return Math.Max(0, data.Count);
			}
			catch
			{
				return 0;
			}
		}

		private static int Reward(VendorOrderDef def)
		{
			try
			{
				return (def.happinessReward != null) ? def.happinessReward.EvaluateInt() : 0;
			}
			catch
			{
				return 0;
			}
		}

		public static int OnPallets(string itemId)
		{
			int num = 0;
			try
			{
				WorldData worldData = MainGame.WorldData;
				if (worldData == null)
				{
					return 0;
				}
				num += Count(worldData.GetWorldZoneDataById("warehouse"), itemId);
				num += Count(worldData.GetWorldZoneDataById("warehouse_cellar"), itemId);
			}
			catch
			{
			}
			return num;
		}

		private static int Count(WorldZoneData zone, string itemId)
		{
			try
			{
				return (zone != null) ? zone.CountItemsOnTownPalettes(itemId) : 0;
			}
			catch
			{
				return 0;
			}
		}

		public static string VendorName(Vendor vendor)
		{
			if (vendor == null)
			{
				return null;
			}
			string id = ((ObjectLinkedToDefinition<VendorDef>)(object)vendor).id;
			string text = GameApi.L(id);
			if (!string.IsNullOrEmpty(text) && text != id)
			{
				return text;
			}
			try
			{
				int num = Math.Max(1, vendor.CurTier);
				List<TownBuildingDef> townBuildingDefs = GameBalance.Me.townBuildingDefs;
				if (townBuildingDefs != null)
				{
					string text2 = null;
					string text3 = null;
					int num2 = 0;
					foreach (TownBuildingDef item in townBuildingDefs)
					{
						if (item != null && !(item.vendorId != id))
						{
							num2++;
							if (text2 == null)
							{
								text2 = ((BalanceBaseObject)item).id;
							}
							if (num2 == num)
							{
								text3 = ((BalanceBaseObject)item).id;
								break;
							}
						}
					}
					string text4 = text3 ?? text2;
					if (text4 != null)
					{
						string text5 = GameApi.L(text4);
						if (!string.IsNullOrEmpty(text5) && text5 != text4)
						{
							return text5;
						}
					}
				}
			}
			catch
			{
			}
			return id;
		}

		private static Sprite Portrait(Vendor vendor)
		{
			try
			{
				VendorDef definition = ((ObjectLinkedToDefinition<VendorDef>)(object)vendor).Definition;
				return (definition == null) ? null : definition.Icon;
			}
			catch
			{
				return null;
			}
		}

		public static List<string> UnlockedVendors()
		{
			try
			{
				MainGame instance = MainGame.Instance;
				List<string> list = (((!((Object)(object)instance != (Object)null)) ? null : instance.GameSave)?.knowledgeSystem)?.unlockedVendorsForOrders;
				return (list == null) ? new List<string>() : new List<string>(list);
			}
			catch
			{
				return new List<string>();
			}
		}
	}
	internal static class Pad
	{
		public sealed class Chord
		{
			public readonly List<GameKey> Mods = new List<GameKey>();

			public GameKey Main = GameKey.None;

			public string Spec;

			public bool IsNone => Missing(Main);
		}

		private static bool _broken;

		private static FieldInfo _fPressed;

		private static FieldInfo _fHolded;

		private static FieldInfo _fDirection;

		private static FieldInfo _fInstance;

		private static bool _fieldsResolved;

		private static readonly List<GameKey> _downs = new List<GameKey>();

		private static readonly List<GameKey> _helds = new List<GameKey>();

		private static Vector2 _dir;

		public static bool Owning;

		private static FieldInfo[] _keyFields;

		private static readonly string[] ModifierNames = new string[3] { "LeftTrigger", "RightTrigger", "RightBumper" };

		private static List<GameKey> _modifiers;

		public static bool ExclusivePlain;

		private static float _repeatAt;

		private static int _lastX;

		private static int _lastY;

		public static bool Active
		{
			get
			{
				if (_broken)
				{
					return false;
				}
				try
				{
					return LazyInput.IsGamepadActive;
				}
				catch (Exception e)
				{
					Fail(e);
					return false;
				}
			}
		}

		private static bool Missing(GameKey key)
		{
			return object.ReferenceEquals(key, null) || (Enumeration)(object)key == (Enumeration)(object)GameKey.None;
		}

		public static bool Down(GameKe