Decompiled source of PersistentHeroes v0.1.4

Mods/PersistentHeroes.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using HarmonyLib;
using MelonLoader;
using PersistentHeroes;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: HarmonyDontPatchAll]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: MelonGame(null, "Backpack Hero")]
[assembly: MelonInfo(typeof(Mod), "Persistent Heroes", "0.1.4", "Kospa", null)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace PersistentHeroes;

internal static class BackpackService
{
	private static Transform Root()
	{
		if (!Object.op_Implicit((Object)(object)LevelUpManager.main) || !Object.op_Implicit((Object)(object)LevelUpManager.main.gridParent))
		{
			throw new InvalidOperationException("Нет LevelUpManager.gridParent.");
		}
		return LevelUpManager.main.gridParent;
	}

	private static List<GridSquare> Cells(Transform root)
	{
		return (from c in ((Component)root).GetComponentsInChildren<GridSquare>(false)
			where (Object)(object)((Component)c).transform.parent == (Object)(object)root && !c.isPouch && !Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent<GridBlock>())
			select c).ToList();
	}

	internal static ProgressState Capture(Player player)
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		LevelUpManager main = LevelUpManager.main;
		if (!Object.op_Implicit((Object)(object)main) || main.levelingUp || !Object.op_Implicit((Object)(object)GameManager.main) || !Object.op_Implicit((Object)(object)player.chosenCharacter))
		{
			throw new InvalidOperationException("Рюкзак не готов к сохранению либо идёт расширение.");
		}
		ProgressState progressState = new ProgressState();
		progressState.Level = player.level;
		progressState.Experience = player.experience;
		progressState.Spaces = main.spacesSaved;
		progressState.Gold = GameManager.main.goldAmount;
		ProgressState progressState2 = progressState;
		foreach (GridSquare item in Cells(Root()))
		{
			Vector3 localPosition = ((Component)item).transform.localPosition;
			int num = Mathf.RoundToInt(localPosition.x);
			int num2 = Mathf.RoundToInt(localPosition.y);
			if (Mathf.Abs((float)num - localPosition.x) > 0.01f || Mathf.Abs((float)num2 - localPosition.y) > 0.01f)
			{
				throw new InvalidDataException("Ячейка вне целочисленной сетки: " + localPosition);
			}
			progressState2.Cells.Add(new CellState
			{
				X = num,
				Y = num2,
				Runic = item.isRunic
			});
		}
		foreach (GameObject chosenBlockPrefab in main.chosenBlockPrefabs)
		{
			if (!Object.op_Implicit((Object)(object)chosenBlockPrefab))
			{
				throw new InvalidDataException("Пустая ссылка chosenBlockPrefabs.");
			}
			progressState2.Blocks.Add(((Object)chosenBlockPrefab).name);
		}
		progressState2.Validate();
		if (progressState2.Level >= player.chosenCharacter.levelUps.Count)
		{
			throw new InvalidDataException("Уровень вне таблицы героя: " + progressState2.Level);
		}
		return progressState2;
	}

	internal static void Restore(Player player, ProgressState state)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		state.Validate();
		Transform val = Root();
		LevelUpManager main = LevelUpManager.main;
		GameObject val2 = (GameObject)AccessTools.Field(typeof(Player), "gridPrefab").GetValue(player);
		if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val2.GetComponent<GridSquare>()) || !Object.op_Implicit((Object)(object)player.chosenCharacter) || state.Level >= player.chosenCharacter.levelUps.Count)
		{
			throw new InvalidDataException("Нет префаба GridSquare или уровень несовместим с героем.");
		}
		List<GameObject> list = new List<GameObject>();
		foreach (string name in state.Blocks)
		{
			List<GameObject> gridBlockPrefabs = main.gridBlockPrefabs;
			Predicate<GameObject> match = (GameObject p) => Object.op_Implicit((Object)(object)p) && ((Object)p).name == name;
			GameObject val3 = gridBlockPrefabs.Find(match);
			if (!Object.op_Implicit((Object)(object)val3))
			{
				throw new InvalidDataException("Не найден блок расширения: " + name);
			}
			list.Add(val3);
		}
		Transform[] array = ((IEnumerable)val).Cast<Transform>().ToArray();
		foreach (Transform val4 in array)
		{
			((Component)val4).gameObject.SetActive(false);
			val4.SetParent((Transform)null);
			Object.Destroy((Object)(object)((Component)val4).gameObject);
		}
		foreach (CellState cell in state.Cells)
		{
			GameObject val5 = Object.Instantiate<GameObject>(val2, val);
			val5.transform.localPosition = new Vector3((float)cell.X, (float)cell.Y, 0f);
			GridSquare component = val5.GetComponent<GridSquare>();
			component.isRunic = cell.Runic;
			component.containsItem = false;
			component.itemCountsAsEmpty = false;
			component.isPouch = false;
			component.itemPouch = null;
			component.SetColor();
			component.GetGridObject();
			GridObject component2 = val5.GetComponent<GridObject>();
			component2.type = (Type)1;
		}
		player.level = state.Level;
		player.experience = state.Experience;
		main.spacesSaved = state.Spaces;
		main.chosenBlockPrefabs = list;
		GameManager.main.goldAmount = state.Gold;
		GameManager.main.ChangeGold(0);
		player.AddExperience(0, ((Component)player).transform.position);
		ModularBackpack.SetAllBackpackSprites();
		main.ResizeAllBackpacks();
		MelonCoroutines.Start(ValidateAfterStart(player, val, state));
	}

	private static IEnumerator ValidateAfterStart(Player player, Transform root, ProgressState state)
	{
		yield return null;
		yield return (object)new WaitForEndOfFrame();
		int frame = 0;
		while (true)
		{
			if (frame < 180)
			{
				if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)root))
				{
					break;
				}
				bool valid;
				try
				{
					List<GridSquare> list = Cells(root);
					foreach (GridSquare item in list)
					{
						item.GetGridObject();
						((Component)item).GetComponent<GridObject>().SnapToGrid();
					}
					HashSet<string> hashSet = new HashSet<string>(list.Select((GridSquare c) => Mathf.RoundToInt(((Component)c).transform.localPosition.x) + ":" + Mathf.RoundToInt(((Component)c).transform.localPosition.y)));
					valid = list.Count == state.Cells.Count && hashSet.SetEquals(state.Cells.Select((CellState c) => c.Key)) && list.All((GridSquare c) => ((Component)c).GetComponent<GridObject>().gridPositions.Count > 0);
					if (valid)
					{
						List<Vector2Int> list2 = list.SelectMany((GridSquare c) => ((Component)c).GetComponent<GridObject>().gridPositions).ToList();
						valid = list2.Distinct().Count() == list2.Count;
					}
				}
				catch (Exception error)
				{
					Mod.RestoreFailed = true;
					Mod.Error("Проверка логической сетки", error);
					break;
				}
				if (valid)
				{
					if (Object.op_Implicit((Object)(object)PocketManager.main))
					{
						PocketManager.main.DeterminePockets();
					}
					MelonLogger.Msg(string.Concat("Восстановлено и проверено: слот=", Singleton.Instance.storyModeSlot, ", герой=", Singleton.Instance.character, ", ячеек=", state.Cells.Count, ", уровень=", state.Level, ", опыт=", state.Experience, ", золото=", state.Gold));
					break;
				}
				yield return null;
				frame++;
				continue;
			}
			Mod.RestoreFailed = true;
			Mod.Notice("Сетка рюкзака не прошла проверку: ожидалось " + state.Cells.Count + " ячеек. Не продолжайте вылазку; пришлите журнал.");
			break;
		}
	}
}
internal static class DeparturePreparation
{
	private static RunTypeSelector selector;

	private static OverworldInventory window;

	private static bool approved;

	private static bool allowSave;

	internal static void Reset()
	{
		PreparationUi.Release();
		selector = null;
		window = null;
		approved = false;
		allowSave = false;
	}

	internal static bool IsApproved(RunTypeSelector value)
	{
		if (approved)
		{
			return (Object)(object)selector == (Object)(object)value;
		}
		return false;
	}

	internal static bool BeforeSave(RunTypeSelector value)
	{
		if (allowSave && IsApproved(value))
		{
			allowSave = false;
			return true;
		}
		Begin(value);
		return false;
	}

	internal static void Begin(RunTypeSelector value)
	{
		if (Mod.Departing || approved || (Object.op_Implicit((Object)(object)window) && ((Component)window).gameObject.activeInHierarchy))
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)value.selectedRunTypeButton) || !value.selectedRunTypeButton.UnlockedRunType())
		{
			throw new InvalidOperationException("Задание недоступно.");
		}
		selector = value;
		try
		{
			window = LoadoutService.OpenPicker();
			if (!Object.op_Implicit((Object)(object)window))
			{
				throw new InvalidOperationException("Игра не создала окно склада.");
			}
			PreparationUi.Create(window, Confirm, Cancel);
			LoadoutService.RefreshSummary();
		}
		catch
		{
			Reset();
			try
			{
				Overworld_InventoryManager.main.HideInventory();
			}
			catch (Exception error)
			{
				Mod.Error("Закрытие склада после ошибки подготовки", error);
			}
			throw;
		}
	}

	private static void Confirm()
	{
		if (approved || Mod.Departing || !Object.op_Implicit((Object)(object)window) || !((Component)window).gameObject.activeInHierarchy)
		{
			return;
		}
		try
		{
			if (!Mod.Eligible())
			{
				throw new InvalidOperationException("Задание больше не поддерживает выбор снаряжения.");
			}
			LoadoutService.ValidateSelection(selector);
			approved = true;
			Overworld_InventoryManager.main.HideInventory();
			window = null;
			allowSave = true;
			MelonLogger.Msg("Снаряжение подтверждено; " + Mod.Context());
			selector.SaveOverworldAndGoAdventuring();
		}
		catch (Exception ex)
		{
			Reset();
			Mod.Error("Подтверждение снаряжения", ex);
			Mod.Notice("Отправка отменена: " + ex.Message);
		}
	}

	private static void Cancel()
	{
		if (!approved)
		{
			Overworld_InventoryManager.main.HideInventory();
		}
	}

	internal static void OnInventoryRemoved()
	{
		if (!approved && !LoadoutService.BuildingPicker && Object.op_Implicit((Object)(object)selector))
		{
			Reset();
			MelonLogger.Msg("Выбор снаряжения закрыт. Отправка отменена, склад не изменён.");
		}
	}
}
[HarmonyPatch(typeof(RunTypeSelector), "SaveOverworldAndGoAdventuring")]
internal static class PreparationPatch
{
	private static bool Prefix(RunTypeSelector __instance)
	{
		if (!Mod.Eligible())
		{
			return true;
		}
		try
		{
			return DeparturePreparation.BeforeSave(__instance);
		}
		catch (Exception ex)
		{
			DeparturePreparation.Reset();
			Mod.Error("Подготовка вылазки", ex);
			Mod.Notice("Не удалось открыть снаряжение: " + ex.Message);
			return false;
		}
	}
}
[HarmonyPatch(typeof(Overworld_InventoryManager), "RemoveAllInventories")]
internal static class PreparationClosePatch
{
	private static void Prefix()
	{
		DeparturePreparation.OnInventoryRemoved();
	}
}
internal static class LoadoutService
{
	private static readonly LoadoutSelection selection = new LoadoutSelection();

	private static readonly List<GameObject> pending = new List<GameObject>();

	private static readonly List<GameObject> pendingCarvings = new List<GameObject>();

	private static Missions mission;

	private static CharacterName hero;

	private static int slot;

	private static OverworldInventory picker;

	private static bool buildingPicker;

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

	private static readonly Dictionary<Overworld_InventoryItemButton, int> cardCopies = new Dictionary<Overworld_InventoryItemButton, int>();

	internal static bool BuildingPicker => buildingPicker;

	internal static void Clear()
	{
		selection.Clear();
		pending.Clear();
		pendingCarvings.Clear();
		mission = null;
		picker = null;
		cardCopies.Clear();
		pageCopies.Clear();
		DeparturePreparation.Reset();
	}

	private static List<string> Stock()
	{
		return ((IEnumerable<string>)MetaProgressSaveManager.main.storedItems).Select((Func<string, string>)Item2.GetDisplayName).ToList();
	}

	internal static void AddControl(RunTypeButton source)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		selection.Clear();
		picker = null;
		DeparturePreparation.Reset();
		mission = source.mission;
		hero = Singleton.Instance.character;
		slot = Singleton.Instance.storyModeSlot;
	}

	private static void UpdateHeading()
	{
		PreparationUi.UpdateCount(selection.Count);
	}

	internal static OverworldInventory OpenPicker()
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Expected O, but got Unknown
		try
		{
			if (Mod.Departing || !Mod.Eligible())
			{
				throw new InvalidOperationException("Сейчас нельзя выбрать снаряжение.");
			}
			if ((Object)(object)Singleton.Instance.mission != (Object)(object)mission || Singleton.Instance.character != hero || Singleton.Instance.storyModeSlot != slot)
			{
				selection.Clear();
				mission = Singleton.Instance.mission;
				hero = Singleton.Instance.character;
				slot = Singleton.Instance.storyModeSlot;
			}
			buildingPicker = true;
			picker = (OverworldInventory)AccessTools.Method(typeof(Overworld_InventoryManager), "ShowInventory", (Type[])null, (Type[])null).Invoke(Overworld_InventoryManager.main, new object[1] { (object)(ClickAction)0 });
			MelonLogger.Msg("Открыт выбор снаряжения; " + Mod.Context());
			return picker;
		}
		finally
		{
			buildingPicker = false;
		}
	}

	internal static void RefreshSummary()
	{
		UpdateHeading();
	}

	internal static void ValidateSelection(RunTypeSelector selector)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)selector) || !Object.op_Implicit((Object)(object)selector.selectedRunTypeButton) || !selector.selectedRunTypeButton.UnlockedRunType())
		{
			throw new InvalidOperationException("Задание недоступно.");
		}
		if ((Object)(object)mission != (Object)(object)Singleton.Instance.mission || hero != Singleton.Instance.character || slot != Singleton.Instance.storyModeSlot)
		{
			throw new InvalidOperationException("Изменились герой, слот или задание. Выберите снаряжение заново.");
		}
		selection.Remaining(Stock());
	}

	internal static bool IsPickerButton(Overworld_InventoryItemButton button)
	{
		if (!buildingPicker)
		{
			if (Object.op_Implicit((Object)(object)picker))
			{
				return (Object)(object)((Component)button).GetComponentInParent<OverworldInventory>() == (Object)(object)picker;
			}
			return false;
		}
		return true;
	}

	internal static void BeginPage(OverworldInventory inventory, int startingNumber)
	{
		if (!buildingPicker && (Object)(object)inventory != (Object)(object)picker)
		{
			return;
		}
		cardCopies.Clear();
		pageCopies.Clear();
		List<string> list = (List<string>)AccessTools.Field(typeof(OverworldInventory), "originOfItems").GetValue(inventory);
		if (list == null)
		{
			throw new InvalidOperationException("Нет списка предметов страницы склада.");
		}
		foreach (string item in list.Take(startingNumber))
		{
			string displayName = Item2.GetDisplayName(item);
			pageCopies.TryGetValue(displayName, out var value);
			pageCopies[displayName] = value + 1;
		}
	}

	internal static void ConfigureButton(Overworld_InventoryItemButton itemButton)
	{
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Expected O, but got Unknown
		if (!IsPickerButton(itemButton) || !Object.op_Implicit((Object)(object)itemButton.item))
		{
			return;
		}
		if (!cardCopies.ContainsKey(itemButton))
		{
			string displayName = Item2.GetDisplayName(((Object)itemButton.item).name);
			pageCopies.TryGetValue(displayName, out var value);
			cardCopies.Add(itemButton, value);
			pageCopies[displayName] = value + 1;
		}
		itemButton.draggable = false;
		Button component = ((Component)itemButton).GetComponent<Button>();
		if (!Object.op_Implicit((Object)(object)component))
		{
			throw new InvalidOperationException("У предмета нет Button: " + ((Object)itemButton).name);
		}
		((UnityEventBase)component.onClick).RemoveAllListeners();
		((UnityEvent)component.onClick).AddListener((UnityAction)delegate
		{
			try
			{
				if (!Mod.Departing)
				{
					if (!CanTake(itemButton.item))
					{
						Mod.Notice("Предмет уникален или недоступен выбранному герою: " + Item2.GetDisplayName(((Object)itemButton.item).name));
					}
					else
					{
						if (!cardCopies.TryGetValue(itemButton, out var value2))
						{
							throw new InvalidOperationException("Карточка склада устарела.");
						}
						selection.Toggle(Item2.GetDisplayName(((Object)itemButton.item).name), value2, Stock());
						MelonLogger.Msg("Снаряжение: предмет=" + Item2.GetDisplayName(((Object)itemButton.item).name) + ", выбрано=" + selection.Get(Item2.GetDisplayName(((Object)itemButton.item).name)) + ", всего=" + selection.Count);
						UpdateHeading();
						if (Object.op_Implicit((Object)(object)picker))
						{
							Overworld_InventoryItemButton[] componentsInChildren = ((Component)picker).GetComponentsInChildren<Overworld_InventoryItemButton>();
							foreach (Overworld_InventoryItemButton button in componentsInChildren)
							{
								UpdateLabel(button);
							}
						}
					}
				}
			}
			catch (Exception error)
			{
				Mod.Error("Выбор предмета", error);
				Mod.Notice("Склад изменился. Закройте выбор и проверьте снаряжение.");
			}
		});
		UpdateLabel(itemButton);
	}

	private static void UpdateLabel(Overworld_InventoryItemButton button)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		if (Object.op_Implicit((Object)(object)button.item) && cardCopies.TryGetValue(button, out var value))
		{
			string displayName = Item2.GetDisplayName(((Object)button.item).name);
			TextMeshProUGUI val = (TextMeshProUGUI)AccessTools.Field(typeof(Overworld_InventoryItemButton), "itemNameText").GetValue(button);
			if (!Object.op_Implicit((Object)(object)val))
			{
				throw new InvalidOperationException("Нет текста карточки снаряжения: " + displayName);
			}
			((TMP_Text)val).text = LangaugeManager.main.GetTextByKey(displayName);
			SelectionMarkerUi.Refresh(button, selection.Contains(displayName, value));
		}
	}

	private static bool CanTake(Item2 item)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Invalid comparison between Unknown and I4
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)item) || (int)item.oneOfAKindType == 2)
		{
			return false;
		}
		if (item.validForCharacters == null)
		{
			Debug.LogWarning((object)("[PersistentHeroes] Нет validForCharacters у предмета " + ((Object)item).name + "; герой=" + Singleton.Instance.character));
			return false;
		}
		if (item.validForCharacters.Count != 0 && !item.validForCharacters.Contains((CharacterName)0))
		{
			return item.validForCharacters.Contains(Singleton.Instance.character);
		}
		return true;
	}

	internal static void Depart(RunTypeSelector selector)
	{
		if (Mod.Departing)
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)SceneLoader.main) || SceneLoader.main.IsLoading())
		{
			throw new InvalidOperationException("Переход между сценами уже выполняется.");
		}
		ValidateSelection(selector);
		MetaProgressSaveManager main = MetaProgressSaveManager.main;
		List<string> list = main.storedItems.ToList();
		selection.Remaining(Stock());
		List<string> list2 = list.ToList();
		List<GameObject> list3 = new List<GameObject>();
		foreach (string key in selection.Selected())
		{
			Predicate<string> match = (string x) => Item2.GetDisplayName(x) == key;
			int num = list2.FindIndex(match);
			if (num < 0)
			{
				throw new InvalidOperationException("На складе больше нет: " + key);
			}
			Item2 item2ByName = DebugItemManager.main.GetItem2ByName(key);
			if (!CanTake(item2ByName))
			{
				throw new InvalidOperationException("Предмет недоступен для вылазки: " + key);
			}
			list3.Add(((Component)item2ByName).gameObject);
			list2.RemoveAt(num);
		}
		Mod.Departing = true;
		try
		{
			main.storedItems = list2;
			SaveStockAndVerify(main, list2);
		}
		catch
		{
			main.storedItems = list;
			try
			{
				SaveStockAndVerify(main, list);
			}
			catch (Exception error)
			{
				Mod.Error("Не удалось записать возврат снаряжения после ошибки сохранения", error);
			}
			Mod.Departing = false;
			throw;
		}
		pending.Clear();
		pending.AddRange(list3);
		Singleton.Instance.otherPrefabObjectsToSpawn.Clear();
		Singleton.Instance.loadSave = false;
		Singleton.Instance.doTutorial = false;
		MelonLogger.Msg("Отправка: слот=" + Singleton.Instance.storyModeSlot + ", предметов=" + pending.Count + ", на складе=" + list2.Count);
		try
		{
			SceneLoader.main.LoadScene("Game", (LoadSceneMode)0, (SceneLoaderPredicate)null, (Action)null);
			selection.Clear();
		}
		catch
		{
			pending.Clear();
			main.storedItems = list;
			Mod.Departing = false;
			SaveStockAndVerify(main, list);
			throw;
		}
	}

	private static void SaveStockAndVerify(MetaProgressSaveManager meta, List<string> expected)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		meta.SaveAll();
		ES3Settings val = (ES3Settings)AccessTools.Field(typeof(MetaProgressSaveManager), "settings").GetValue(meta);
		byte[] first = ES3.DecryptBytes(File.ReadAllBytes(val.path), (string)null);
		if (!first.SequenceEqual(ES3.LoadRawBytes(val)))
		{
			throw new IOException("Записанные данные склада отличаются от кэша: " + val.path);
		}
		List<string> first2 = ES3.Load<List<string>>("storedItems", val);
		if (!first2.SequenceEqual(expected))
		{
			throw new IOException("Склад на диске не совпадает с выбранным снаряжением: " + val.path);
		}
	}

	internal static void Spawn()
	{
		//IL_0083: 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)
		if (!Mod.Supported())
		{
			return;
		}
		if (Singleton.Instance.loadSave)
		{
			pending.Clear();
			pendingCarvings.Clear();
		}
		else
		{
			if (pending.Count == 0)
			{
				return;
			}
			GameObject[] array = pending.ToArray();
			pending.Clear();
			GameObject[] array2 = array;
			foreach (GameObject val in array2)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					throw new InvalidOperationException("Потерян префаб выбранного снаряжения.");
				}
				if (Object.op_Implicit((Object)(object)val.GetComponent<Carving>()))
				{
					pendingCarvings.Add(val);
				}
				else
				{
					Object.Instantiate<GameObject>(val, Vector3.zero, Quaternion.identity);
				}
			}
			MelonLogger.Msg("Выдано снаряжение: " + array.Length + " предметов. Повторная выдача заблокирована.");
		}
	}

	internal static void SpawnCarvings(Tote deck)
	{
		if (!Singleton.Instance.loadSave && pendingCarvings.Count != 0)
		{
			GameObject[] array = pendingCarvings.ToArray();
			pendingCarvings.Clear();
			GameObject[] array2 = array;
			foreach (GameObject val in array2)
			{
				deck.AddCarvingToDeck(val);
			}
			MelonLogger.Msg("В стартовую колоду передано резных фигурок: " + array.Length);
		}
	}
}
[HarmonyPatch(typeof(RunTypeButton), "SetRunType")]
internal static class MissionUiPatch
{
	private static void Prefix(RunTypeButton __instance, out List<RunProperty> __state)
	{
		__state = null;
		if (Mod.Supported() && Object.op_Implicit((Object)(object)__instance.mission) && !__instance.mission.runProperties.Exists((RunProperty p) => (int)p.type == 42 || (int)p.type == 45 || (int)p.type == 56 || (int)p.type == 28))
		{
			__state = __instance.mission.runProperties;
			__instance.mission.runProperties = __state.Where((RunProperty p) => (int)p.type != 58).ToList();
		}
	}

	private static void Postfix(RunTypeButton __instance, List<RunProperty> __state)
	{
		if (__state == null)
		{
			return;
		}
		__instance.mission.runProperties = __state;
		try
		{
			LoadoutService.AddControl(__instance);
		}
		catch (Exception error)
		{
			Mod.Error("Создание выбора снаряжения", error);
			Mod.Notice("Не удалось создать выбор снаряжения.");
		}
	}

	private static Exception Finalizer(RunTypeButton __instance, List<RunProperty> __state, Exception __exception)
	{
		if (__state != null && Object.op_Implicit((Object)(object)__instance.mission))
		{
			__instance.mission.runProperties = __state;
		}
		return __exception;
	}
}
[HarmonyPatch(typeof(Overworld_InventoryItemButton), "SetupAction")]
internal static class PickerButtonPatch
{
	private static void Postfix(Overworld_InventoryItemButton __instance)
	{
		try
		{
			LoadoutService.ConfigureButton(__instance);
		}
		catch (Exception error)
		{
			Mod.Error("Настройка кнопки предмета", error);
		}
	}
}
[HarmonyPatch(typeof(OverworldInventory), "Setup")]
internal static class PickerStockPatch
{
	private static void Prefix(ref List<string> originOfItems)
	{
		if (LoadoutService.BuildingPicker)
		{
			originOfItems = new List<string>(originOfItems);
		}
	}
}
[HarmonyPatch(typeof(OverworldInventory), "SetupItemsForInventory")]
internal static class PickerPagePatch
{
	private static void Prefix(OverworldInventory __instance, int startingNumber)
	{
		LoadoutService.BeginPage(__instance, startingNumber);
	}
}
[HarmonyPatch(typeof(Overworld_InventoryItemButton), "RightClick")]
internal static class PickerContextPatch
{
	private static bool Prefix(Overworld_InventoryItemButton __instance)
	{
		return !LoadoutService.IsPickerButton(__instance);
	}
}
[HarmonyPatch(typeof(RunTypeSelector), "GoAdventuring2")]
internal static class DeparturePatch
{
	private static bool Prefix(RunTypeSelector __instance)
	{
		if (!Mod.Eligible())
		{
			return true;
		}
		try
		{
			if (!DeparturePreparation.IsApproved(__instance))
			{
				DeparturePreparation.Begin(__instance);
				return false;
			}
			LoadoutService.Depart(__instance);
			DeparturePreparation.Reset();
		}
		catch (Exception ex)
		{
			DeparturePreparation.Reset();
			Mod.Error("Отправка отменена", ex);
			Mod.Notice("Отправка отменена: " + ex.Message);
		}
		return false;
	}
}
[HarmonyPatch(typeof(RunTypeManager), "CreateStarterItems")]
internal static class StarterPatch
{
	private static void Postfix()
	{
		try
		{
			LoadoutService.Spawn();
		}
		catch (Exception error)
		{
			Mod.Error("Выдача снаряжения", error);
			Mod.Notice("Не удалось выдать всё снаряжение. Пришлите журнал мода.");
		}
	}
}
[HarmonyPatch(typeof(Tote), "SpawnTote")]
internal static class StarterDeckPatch
{
	private static void Prefix(Tote __instance)
	{
		try
		{
			LoadoutService.SpawnCarvings(__instance);
		}
		catch (Exception error)
		{
			Mod.Error("Подготовка колоды", error);
			Mod.Notice("Не удалось подготовить колоду. Пришлите журнал мода.");
		}
	}
}
public sealed class Mod : MelonMod
{
	internal static ProgressStore Store;

	internal static bool RestoreFailed;

	internal static Player RestoredPlayer;

	internal static bool Departing;

	public override void OnInitializeMelon()
	{
		Store = new ProgressStore(Path.Combine(Application.persistentDataPath, "PersistentHeroesV2"));
		try
		{
			((MelonBase)this).HarmonyInstance.PatchAll(typeof(Mod).Assembly);
			MelonLogger.Msg("Persistent Heroes 0.1.4: сюжетный режим, все герои. Победа — сохранить, смерть — сброс, выход — без изменений.");
			MelonLogger.Msg("Папка прогресса: " + Path.Combine(Application.persistentDataPath, "PersistentHeroesV2"));
			string text = Path.Combine(Environment.CurrentDirectory, "UserData", "PersistentHeroes");
			if (Directory.Exists(text))
			{
				Notice("Обнаружены старые данные " + text + ". Они не импортируются: результат вылазок в старом формате неизвестен.");
			}
		}
		catch (Exception error)
		{
			((MelonBase)this).HarmonyInstance.UnpatchSelf();
			Error("Подключение мода отменено: несовместимый API игры", error);
		}
	}

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		if (sceneName == "Game")
		{
			RestoreFailed = false;
			RestoredPlayer = null;
			Departing = false;
		}
		if (sceneName == "Overworld" || sceneName == "MainMenu")
		{
			LoadoutService.Clear();
			Departing = false;
		}
	}

	internal static bool Supported()
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)Singleton.Instance) && Singleton.Instance.storyMode && (int)Singleton.Instance.character != 0)
		{
			return Singleton.Instance.storyModeSlot >= 0;
		}
		return false;
	}

	internal static bool Eligible()
	{
		return EligibilityReason(Object.op_Implicit((Object)(object)Singleton.Instance) ? Singleton.Instance.mission : null, victory: false) == null;
	}

	internal static string EligibilityReason(Missions mission, bool victory)
	{
		if (Object.op_Implicit((Object)(object)mission) && mission.runProperties == null)
		{
			return "у задания отсутствует список правил: " + ((Object)mission).name;
		}
		return ProgressPolicy.BlockReason(Supported(), Object.op_Implicit((Object)(object)mission), Object.op_Implicit((Object)(object)mission) && mission.runProperties.Exists((RunProperty p) => (int)p.type == 56), Object.op_Implicit((Object)(object)mission) && mission.runProperties.Exists((RunProperty p) => (int)p.type == 28), Object.op_Implicit((Object)(object)mission) && mission.runProperties.Exists((RunProperty p) => (int)p.type == 42), Object.op_Implicit((Object)(object)mission) && mission.runProperties.Exists((RunProperty p) => (int)p.type == 45), victory);
	}

	internal static string Context()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)Singleton.Instance))
		{
			return "Singleton отсутствует";
		}
		return string.Concat("слот=", Singleton.Instance.storyModeSlot, ", герой=", Singleton.Instance.character, ", задание=", Object.op_Implicit((Object)(object)Singleton.Instance.mission) ? ((Object)Singleton.Instance.mission).name : "отсутствует");
	}

	internal static void Error(string operation, Exception error)
	{
		string text = Context();
		MelonLogger.Error(operation + "; " + text + ": " + error);
		Debug.LogError((object)("[PersistentHeroes] " + operation + "; " + text + ": " + error));
	}

	internal static void Notice(string text)
	{
		MelonLogger.Warning(text);
		Debug.LogWarning((object)("[PersistentHeroes] " + text));
		if (Object.op_Implicit((Object)(object)PopUpManager.main))
		{
			PopUpManager.main.CreatePopUp(text);
		}
	}
}
[HarmonyPatch(typeof(Player), "SetBagSize")]
internal static class BagPatch
{
	private static bool Prefix(Player __instance)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		string text = Mod.EligibilityReason(Object.op_Implicit((Object)(object)Singleton.Instance) ? Singleton.Instance.mission : null, victory: false);
		if (text != null || Singleton.Instance.loadSave || Singleton.Instance.doTutorial)
		{
			MelonLogger.Msg("Восстановление пропущено: " + (text ?? (Singleton.Instance.loadSave ? "продолжение сохранённой вылазки" : "активное обучение")) + "; " + Mod.Context());
			return true;
		}
		if ((Object)(object)Mod.RestoredPlayer == (Object)(object)__instance)
		{
			return false;
		}
		try
		{
			ProgressState progressState = Mod.Store.Load(Singleton.Instance.storyModeSlot, ((object)Singleton.Instance.character).ToString());
			if (progressState == null || progressState.Dead)
			{
				MelonLogger.Msg("Обычный старт: " + ((progressState == null) ? "ещё нет сохранённой победы" : "прогресс сброшен после смерти или удаления слота") + "; " + Mod.Context());
				return true;
			}
			BackpackService.Restore(__instance, progressState);
			Mod.RestoredPlayer = __instance;
			return false;
		}
		catch (Exception error)
		{
			Mod.RestoreFailed = true;
			Mod.Error("Не удалось восстановить рюкзак", error);
			Mod.Notice("Не удалось восстановить прогресс. Не продолжайте вылазку; пришлите журнал мода. Старое состояние не будет перезаписано победой.");
			return true;
		}
	}
}
[HarmonyPatch(typeof(MetaProgressSaveManager), "CompleteMission")]
internal static class VictoryPatch
{
	private static void Postfix(Missions m)
	{
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		string text = Mod.EligibilityReason(m, victory: true);
		if (text != null || !Object.op_Implicit((Object)(object)Player.main) || Mod.RestoreFailed)
		{
			Mod.Notice("Сохранение победы пропущено: " + (text ?? ((!Object.op_Implicit((Object)(object)Player.main)) ? "нет Player" : "сетка ранее не прошла проверку")) + "; " + Mod.Context());
			return;
		}
		try
		{
			ProgressState progressState = BackpackService.Capture(Player.main);
			Mod.Store.Save(Singleton.Instance.storyModeSlot, ((object)Singleton.Instance.character).ToString(), progressState);
			MelonLogger.Msg("Победа: слот=" + Singleton.Instance.storyModeSlot + ", уровень=" + progressState.Level + ", опыт=" + progressState.Experience + ", золото=" + progressState.Gold + ", ячеек=" + progressState.Cells.Count + ", свободных расширений=" + progressState.Spaces);
		}
		catch (Exception error)
		{
			Mod.Error("Не удалось сохранить победу", error);
			Mod.Notice("Прогресс победы не сохранён. Пришлите журнал мода.");
		}
	}
}
[HarmonyPatch(typeof(MetaProgressSaveManager), "SaveLastRun")]
internal static class OutcomePatch
{
	private static void Postfix(Result result)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Invalid comparison between Unknown and I4
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		if (!Mod.Supported())
		{
			return;
		}
		if ((int)result == 3)
		{
			MelonLogger.Msg("Добровольный выход: постоянный прогресс не изменён.");
		}
		if ((int)result != 2)
		{
			return;
		}
		try
		{
			Mod.Store.Reset(Singleton.Instance.storyModeSlot, ((object)Singleton.Instance.character).ToString());
			LoadStoryGame.DeleteStoryRunSave(Singleton.Instance.storyModeSlot);
			MelonLogger.Msg(string.Concat("Смерть: прогресс ", Singleton.Instance.character, " сброшен в слоте ", Singleton.Instance.storyModeSlot));
		}
		catch (Exception error)
		{
			Mod.Error("Не удалось записать полный сброс после смерти", error);
			Mod.Notice("Сброс после смерти не записан. Не начинайте новую вылазку; пришлите журнал.");
		}
	}
}
[HarmonyPatch(typeof(SaveAnItemOnLoss), "SaveItem")]
internal static class DeathItemPatch
{
	private static bool Prefix()
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Invalid comparison between Unknown and I4
		if (!Mod.Supported() || !Object.op_Implicit((Object)(object)MetaProgressSaveManager.main) || MetaProgressSaveManager.main.lastRun == null || (int)MetaProgressSaveManager.main.lastRun.result != 2)
		{
			return true;
		}
		MelonLogger.Msg("Смерть: возврат предмета отключён правилом полной потери.");
		return false;
	}
}
[HarmonyPatch(typeof(SaveAnItemOnLoss), "GetItem")]
internal static class DeathItemSelectionPatch
{
	private static bool Prefix()
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Invalid comparison between Unknown and I4
		if (!Mod.Supported() || !Object.op_Implicit((Object)(object)MetaProgressSaveManager.main) || MetaProgressSaveManager.main.lastRun == null || (int)MetaProgressSaveManager.main.lastRun.result != 2)
		{
			return true;
		}
		Mod.Notice("При смерти теряется всё снаряжение вылазки. Предметы, оставленные на городском складе, сохраняются.");
		return false;
	}
}
[HarmonyPatch(typeof(LoadStoryGame), "DeleteStorySaveFile")]
internal static class DeleteSlotPatch
{
	private static void Postfix(int slotNumber)
	{
		try
		{
			Mod.Store.ResetSlot(slotNumber);
		}
		catch (Exception error)
		{
			Mod.Error("Не удалось очистить прогресс удалённого слота " + slotNumber, error);
		}
	}
}
internal static class PreparationUi
{
	private static ReplacementText summary;

	private static readonly List<GameObject> hiddenCloseButtons = new List<GameObject>();

	internal static void Release()
	{
		foreach (GameObject hiddenCloseButton in hiddenCloseButtons)
		{
			if (Object.op_Implicit((Object)(object)hiddenCloseButton))
			{
				hiddenCloseButton.SetActive(true);
			}
		}
		hiddenCloseButtons.Clear();
		summary = null;
	}

	internal static void Create(OverworldInventory inventory, Action confirm, Action cancel)
	{
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		//IL_0284: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		Button[] array = Object.FindObjectsOfType<Button>();
		foreach (Button val in array)
		{
			for (int j = 0; j < ((UnityEventBase)val.onClick).GetPersistentEventCount(); j++)
			{
				if (((UnityEventBase)val.onClick).GetPersistentTarget(j) == (Object)(object)Overworld_InventoryManager.main && ((UnityEventBase)val.onClick).GetPersistentMethodName(j) == "HideInventory")
				{
					hiddenCloseButtons.Add(((Component)val).gameObject);
					((Component)val).gameObject.SetActive(false);
					break;
				}
			}
		}
		if (hiddenCloseButtons.Count == 0)
		{
			Debug.LogWarning((object)"[PersistentHeroes] Не найдена штатная кнопка HideInventory для замены на экране снаряжения.");
		}
		TextMeshProUGUI componentInChildren = ((Component)inventory).GetComponentInChildren<TextMeshProUGUI>(true);
		ScrollRect componentInChildren2 = ((Component)inventory).GetComponentInChildren<ScrollRect>(true);
		if (!Object.op_Implicit((Object)(object)componentInChildren) || !Object.op_Implicit((Object)(object)((TMP_Text)componentInChildren).font) || !Object.op_Implicit((Object)(object)componentInChildren2))
		{
			throw new InvalidOperationException("У склада отсутствует шрифт или область прокрутки: " + ((Object)inventory).name);
		}
		RectTransform val2 = (RectTransform)((Component)componentInChildren2).transform;
		val2.offsetMin += new Vector2(0f, 190f);
		RectTransform val3 = Rect("PersistentHeroesPreparation", ((Component)inventory).transform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(40f, 80f), new Vector2(-40f, 260f));
		((Component)val3).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
		((Graphic)((Component)val3).gameObject.AddComponent<Image>()).color = new Color(0.12f, 0.09f, 0.07f, 0.98f);
		TextMeshProUGUI text = Text((Transform)(object)val3, ((TMP_Text)componentInChildren).font, "", new Vector2(12f, 86f), new Vector2(-12f, -6f));
		summary = Localize(text, "items");
		AddButton((Transform)(object)val3, ((TMP_Text)componentInChildren).font, "b9", right: false, cancel);
		AddButton((Transform)(object)val3, ((TMP_Text)componentInChildren).font, "b8", right: true, confirm);
		LangaugeManager.main.SetFont((Transform)(object)val3);
		TextMeshProUGUI[] componentsInChildren = ((Component)val3).GetComponentsInChildren<TextMeshProUGUI>();
		foreach (TextMeshProUGUI val4 in componentsInChildren)
		{
			((TMP_Text)val4).lineSpacing = 0f;
		}
		Canvas.ForceUpdateCanvases();
		Rect rect = val3.rect;
		if (!(((Rect)(ref rect)).width < 400f))
		{
			Rect rect2 = val2.rect;
			if (!(((Rect)(ref rect2)).height <= 0f))
			{
				return;
			}
		}
		throw new InvalidOperationException(string.Concat("Недостаточно места для снаряжения: панель=", val3.rect, ", прокрутка=", val2.rect));
	}

	internal static void UpdateCount(int count)
	{
		if (Object.op_Implicit((Object)(object)summary))
		{
			summary.nonKeySuffix = ": " + count;
			summary.ReplaceText();
		}
	}

	private static ReplacementText Localize(TextMeshProUGUI text, string key)
	{
		ReplacementText val = ((Component)text).gameObject.AddComponent<ReplacementText>();
		val.key = key;
		val.ReplaceText();
		return val;
	}

	private static RectTransform Rect(string name, Transform parent, Vector2 min, Vector2 max, Vector2 low, Vector2 high)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
		RectTransform val2 = (RectTransform)val.transform;
		((Transform)val2).SetParent(parent, false);
		val2.anchorMin = min;
		val2.anchorMax = max;
		val2.offsetMin = low;
		val2.offsetMax = high;
		return val2;
	}

	private static TextMeshProUGUI Text(Transform parent, TMP_FontAsset font, string value, Vector2 low, Vector2 high)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		RectTransform val = Rect("Label", parent, Vector2.zero, Vector2.one, low, high);
		TextMeshProUGUI val2 = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>();
		((TMP_Text)val2).font = font;
		((TMP_Text)val2).text = value;
		((TMP_Text)val2).fontSize = 32f;
		((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
		((Graphic)val2).raycastTarget = false;
		((TMP_Text)val2).enableAutoSizing = true;
		((TMP_Text)val2).fontSizeMin = 20f;
		((TMP_Text)val2).fontSizeMax = 32f;
		return val2;
	}

	private static void AddButton(Transform parent, TMP_FontAsset font, string label, bool right, Action action)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Expected O, but got Unknown
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		RectTransform val = Rect(right ? "Confirm" : "Cancel", parent, new Vector2(right ? 0.5f : 0f, 0f), new Vector2(right ? 1f : 0.5f, 0f), new Vector2(12f, 12f), new Vector2(-12f, 78f));
		Image val2 = ((Component)val).gameObject.AddComponent<Image>();
		((Graphic)val2).color = (right ? new Color(0.19f, 0.4f, 0.25f) : new Color(0.36f, 0.27f, 0.19f));
		Button val3 = ((Component)val).gameObject.AddComponent<Button>();
		((Selectable)val3).targetGraphic = (Graphic)(object)val2;
		((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
		{
			action();
		});
		Localize(Text((Transform)(object)val, font, "", new Vector2(10f, 4f), new Vector2(-10f, -4f)), label);
	}
}
internal static class ProgressPolicy
{
	public static string BlockReason(bool supported, bool hasMission, bool disabled, bool puzzle, bool tutorial, bool intro, bool victory)
	{
		if (!supported)
		{
			return "не выбран сюжетный слот и герой";
		}
		if (!hasMission)
		{
			return "нет выбранного задания";
		}
		if (disabled)
		{
			return "метапрогресс отключён правилами задания";
		}
		if (puzzle)
		{
			return "задание-головоломка";
		}
		if (!victory && (tutorial || intro))
		{
			return "старт обучения или сюжетного вступления";
		}
		return null;
	}
}
internal sealed class CellState
{
	public int X;

	public int Y;

	public bool Runic;

	public string Key => X + ":" + Y;
}
internal sealed class ProgressState
{
	public int Level = -1;

	public int Experience;

	public int Spaces;

	public int Gold;

	public bool Dead;

	public List<CellState> Cells = new List<CellState>();

	public List<string> Blocks = new List<string>();

	public void Validate()
	{
		if (Level < -1 || Level > 1000 || Experience < 0 || Spaces < 0 || Spaces > 1024 || Gold < 0)
		{
			throw new InvalidDataException("Некорректные значения прогресса.");
		}
		if (!Dead)
		{
			if (Cells.Count == 0 || Cells.Count > 1024 || Cells.Select((CellState c) => c.Key).Distinct().Count() != Cells.Count)
			{
				throw new InvalidDataException("Пустая сетка, слишком много ячеек или повтор координат.");
			}
			if (Cells.Any((CellState c) => Math.Abs((long)c.X) > 100 || Math.Abs((long)c.Y) > 100) || Blocks.Count > 64)
			{
				throw new InvalidDataException("Недопустимые координаты или список блоков рюкзака.");
			}
		}
	}
}
internal sealed class ProgressStore
{
	private readonly string root;

	public ProgressStore(string root)
	{
		this.root = root;
	}

	public string PathFor(int slot, string hero)
	{
		if (slot < 0 || string.IsNullOrEmpty(hero) || hero.Any((char c) => !char.IsLetterOrDigit(c)))
		{
			throw new InvalidDataException("Некорректный слот или идентификатор героя.");
		}
		return Path.Combine(root, "slot" + slot + "_" + hero + ".ph2");
	}

	public ProgressState Load(int slot, string hero)
	{
		string text = PathFor(slot, hero);
		if (!File.Exists(text))
		{
			return null;
		}
		using FileStream fileStream = File.OpenRead(text);
		using BinaryReader binaryReader = new BinaryReader(fileStream);
		if (binaryReader.ReadString() != "PH02" || binaryReader.ReadInt32() != 1)
		{
			throw new InvalidDataException("Неподдерживаемый формат прогресса: " + text);
		}
		ProgressState progressState = new ProgressState();
		progressState.Dead = binaryReader.ReadBoolean();
		progressState.Level = binaryReader.ReadInt32();
		progressState.Experience = binaryReader.ReadInt32();
		progressState.Spaces = binaryReader.ReadInt32();
		progressState.Gold = binaryReader.ReadInt32();
		ProgressState progressState2 = progressState;
		int num = binaryReader.ReadInt32();
		if (num < 0 || num > 1024)
		{
			throw new InvalidDataException("Некорректное число ячеек.");
		}
		for (int i = 0; i < num; i++)
		{
			progressState2.Cells.Add(new CellState
			{
				X = binaryReader.ReadInt32(),
				Y = binaryReader.ReadInt32(),
				Runic = binaryReader.ReadBoolean()
			});
		}
		num = binaryReader.ReadInt32();
		if (num < 0 || num > 64)
		{
			throw new InvalidDataException("Некорректное число блоков.");
		}
		for (int j = 0; j < num; j++)
		{
			progressState2.Blocks.Add(binaryReader.ReadString());
		}
		if (fileStream.Position != fileStream.Length)
		{
			throw new InvalidDataException("Лишние данные в файле прогресса.");
		}
		progressState2.Validate();
		return progressState2;
	}

	public void Save(int slot, string hero, ProgressState state)
	{
		state.Validate();
		string text = PathFor(slot, hero);
		Directory.CreateDirectory(root);
		string text2 = text + ".tmp";
		using (FileStream fileStream = new FileStream(text2, FileMode.Create, FileAccess.Write, FileShare.None))
		{
			using BinaryWriter binaryWriter = new BinaryWriter(fileStream);
			binaryWriter.Write("PH02");
			binaryWriter.Write(1);
			binaryWriter.Write(state.Dead);
			binaryWriter.Write(state.Level);
			binaryWriter.Write(state.Experience);
			binaryWriter.Write(state.Spaces);
			binaryWriter.Write(state.Gold);
			binaryWriter.Write(state.Cells.Count);
			foreach (CellState cell in state.Cells)
			{
				binaryWriter.Write(cell.X);
				binaryWriter.Write(cell.Y);
				binaryWriter.Write(cell.Runic);
			}
			binaryWriter.Write(state.Blocks.Count);
			foreach (string block in state.Blocks)
			{
				binaryWriter.Write(block);
			}
			binaryWriter.Flush();
			fileStream.Flush(flushToDisk: true);
		}
		if (File.Exists(text))
		{
			File.Replace(text2, text, null);
		}
		else
		{
			File.Move(text2, text);
		}
	}

	public void Reset(int slot, string hero)
	{
		Save(slot, hero, new ProgressState
		{
			Dead = true
		});
	}

	public void ResetSlot(int slot)
	{
		if (slot < 0)
		{
			throw new InvalidDataException("Некорректный слот.");
		}
		if (Directory.Exists(root))
		{
			string text = "slot" + slot + "_";
			string[] files = Directory.GetFiles(root, text + "*.ph2");
			foreach (string path in files)
			{
				Reset(slot, Path.GetFileNameWithoutExtension(path).Substring(text.Length));
			}
		}
	}
}
internal sealed class LoadoutSelection
{
	private readonly Dictionary<string, HashSet<int>> copies = new Dictionary<string, HashSet<int>>(StringComparer.Ordinal);

	public int Count => copies.Values.Sum((HashSet<int> x) => x.Count);

	public int Get(string key)
	{
		if (!copies.TryGetValue(key, out var value))
		{
			return 0;
		}
		return value.Count;
	}

	public bool Contains(string key, int copy)
	{
		if (copies.TryGetValue(key, out var value))
		{
			return value.Contains(copy);
		}
		return false;
	}

	public void Clear()
	{
		copies.Clear();
	}

	public void Toggle(string key, int copy, IList<string> stock)
	{
		if (copy < 0 || copy >= stock.Count((string x) => x == key))
		{
			throw new InvalidOperationException("На складе нет экземпляра: " + key + ", номер=" + copy);
		}
		if (!copies.TryGetValue(key, out var value))
		{
			value = new HashSet<int>();
			copies.Add(key, value);
		}
		if (!value.Add(copy))
		{
			value.Remove(copy);
		}
		if (value.Count == 0)
		{
			copies.Remove(key);
		}
	}

	public List<string> Selected()
	{
		return copies.SelectMany((KeyValuePair<string, HashSet<int>> pair) => Enumerable.Repeat(pair.Key, pair.Value.Count)).ToList();
	}

	public List<string> Remaining(IList<string> normalizedStock)
	{
		foreach (KeyValuePair<string, HashSet<int>> pair in copies)
		{
			KeyValuePair<string, HashSet<int>> keyValuePair = pair;
			if (keyValuePair.Value.Any((int copy) => copy >= normalizedStock.Count(delegate(string x)
			{
				KeyValuePair<string, HashSet<int>> keyValuePair3 = pair;
				return x == keyValuePair3.Key;
			})))
			{
				KeyValuePair<string, HashSet<int>> keyValuePair2 = pair;
				throw new InvalidOperationException("Состав склада изменился: " + keyValuePair2.Key);
			}
		}
		List<string> list = normalizedStock.ToList();
		foreach (string item in Selected())
		{
			if (!list.Remove(item))
			{
				throw new InvalidOperationException("Состав склада изменился: " + item);
			}
		}
		return list;
	}
}
[HarmonyPatch(typeof(GameManager), "EndBattle")]
internal static class RewardLabelPatch
{
	private static void Prefix(GameManager __instance)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		if (Object.op_Implicit((Object)(object)GameFlowManager.main) && GameFlowManager.main.combatEndedAbruptly)
		{
			return;
		}
		try
		{
			GameObject val = (GameObject)AccessTools.Field(typeof(GameManager), "afterBattleButton").GetValue(__instance);
			TextMeshProUGUI val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponentInChildren<TextMeshProUGUI>(true) : null);
			if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)LangaugeManager.main))
			{
				throw new InvalidOperationException("Не найдена кнопка награды или менеджер перевода.");
			}
			((TMP_Text)val2).text = LangaugeManager.main.GetTextByKey("gm14");
		}
		catch (Exception error)
		{
			Mod.Error("Перевод кнопки награды, ключ=gm14", error);
		}
	}
}
internal static class SelectionMarkerUi
{
	private const string MarkerName = "PersistentHeroesSelection";

	private static readonly Color SelectedColor = new Color(0.3f, 0.91f, 0.53f, 1f);

	internal static void Refresh(Overworld_InventoryItemButton card, bool selected)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		Transform val = ((Component)card).transform.Find("PersistentHeroesSelection");
		if (!Object.op_Implicit((Object)(object)val))
		{
			RectTransform val2 = Rect("PersistentHeroesSelection", ((Component)card).transform, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
			((Component)val2).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			Edge((Transform)(object)val2, "Left", Vector2.zero, new Vector2(0f, 1f), Vector2.zero, new Vector2(5f, 0f));
			Edge((Transform)(object)val2, "Right", new Vector2(1f, 0f), Vector2.one, new Vector2(-5f, 0f), Vector2.zero);
			Edge((Transform)(object)val2, "Bottom", Vector2.zero, new Vector2(1f, 0f), Vector2.zero, new Vector2(0f, 5f));
			Edge((Transform)(object)val2, "Top", new Vector2(0f, 1f), Vector2.one, new Vector2(0f, -5f), Vector2.zero);
			val = (Transform)(object)val2;
		}
		((Component)val).gameObject.SetActive(selected);
		val.SetAsLastSibling();
	}

	private static void Edge(Transform parent, string name, Vector2 min, Vector2 max, Vector2 low, Vector2 high)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		Image val = ((Component)Rect(name, parent, min, max, low, high)).gameObject.AddComponent<Image>();
		((Graphic)val).color = SelectedColor;
		((Graphic)val).raycastTarget = false;
	}

	private static RectTransform Rect(string name, Transform parent, Vector2 min, Vector2 max, Vector2 low, Vector2 high)
	{
		//IL_0016: 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: Expected O, but got Unknown
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform;
		((Transform)val).SetParent(parent, false);
		val.anchorMin = min;
		val.anchorMax = max;
		val.offsetMin = low;
		val.offsetMax = high;
		return val;
	}
}