Decompiled source of Shopkeep v1.2.0

Shopkeep.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Shopkeep")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shopkeep")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0b261f31-75b2-4405-b7ec-25e6fade4c16")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Shopkeep;

public class BercansDestroy : MonoBehaviour
{
	private void OnDestroy()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		foreach (Transform item in ((Component)this).transform)
		{
			Transform val = item;
			if (((Object)((Component)val).gameObject).name.Contains("Haldor"))
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
		}
	}
}
[BepInPlugin("com.demonforge.shopkeep", "Shopkeep", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public class Shopkeep : BaseUnityPlugin
{
	public const string PluginGUID = "com.demonforge.shopkeep";

	public const string PluginName = "Shopkeep";

	public const string PluginVersion = "1.1.0";

	public static ManualLogSource Log;

	private readonly Harmony harmony = new Harmony("com.demonforge.shopkeep");

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogInfo((object)"Shopkeep 1.1.0 loading...");
		try
		{
			harmony.PatchAll();
		}
		catch (Exception arg)
		{
			Log.LogError((object)$"Harmony patching failed: {arg}");
		}
		PrefabManager.OnVanillaPrefabsAvailable += RegisterNPC;
		PrefabManager.OnPrefabsRegistered += ValidatePrefabNames;
	}

	private void RegisterNPC()
	{
		Log.LogInfo((object)"Registering Shopkeep NPC...");
		ShopkeepNPC.Register();
		PrefabManager.OnVanillaPrefabsAvailable -= RegisterNPC;
	}

	private void ValidatePrefabNames()
	{
		PrefabManager.OnPrefabsRegistered -= ValidatePrefabNames;
		List<string> list = new List<string>();
		foreach (string prefabName in ShopUI.GetPrefabNames())
		{
			if (!PrefabExists(prefabName))
			{
				list.Add("shop: " + prefabName);
			}
		}
		foreach (string lootPrefabName in ItemEffects.GetLootPrefabNames())
		{
			if (!PrefabExists(lootPrefabName))
			{
				list.Add("loot: " + lootPrefabName);
			}
		}
		if (list.Count == 0)
		{
			Log.LogInfo((object)"Prefab check passed - all shop and loot entries resolved.");
			return;
		}
		Log.LogWarning((object)$"Prefab check found {list.Count} unresolved name(s):");
		foreach (string item in list)
		{
			Log.LogWarning((object)("  " + item));
		}
	}

	private static bool PrefabExists(string name)
	{
		if (PrefabManager.Instance != null && (Object)(object)PrefabManager.Instance.GetPrefab(name) != (Object)null)
		{
			return true;
		}
		if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ZNetScene.instance.GetPrefab(name) != (Object)null)
		{
			return true;
		}
		return false;
	}
}
public static class ItemEffects
{
	public const string VitalityKey = "Shopkeep_Vitality";

	public const string EnduranceKey = "Shopkeep_Endurance";

	public const string BurdenKey = "Shopkeep_Burden";

	public const int MaxScrollUses = 3;

	public const float HealthPerUse = 25f;

	public const float StaminaPerUse = 25f;

	public const float CarryPerUse = 100f;

	private static readonly List<(string item, int amount, float chance)> LootTable = new List<(string, int, float)>
	{
		("Wood", 50, 30f),
		("FineWood", 20, 20f),
		("Coins", 500, 25f),
		("Copper", 10, 15f),
		("Tin", 10, 15f),
		("Bronze", 10, 12f),
		("Iron", 10, 12f),
		("Silver", 10, 10f),
		("BlackMetal", 10, 8f),
		("Flametal", 5, 4f),
		("Chitin", 15, 15f),
		("Crystal", 10, 10f),
		("Ruby", 5, 8f),
		("FreezeGland", 10, 10f),
		("Blueberries", 20, 20f),
		("SerpentStew", 3, 10f),
		("BloodPudding", 3, 10f),
		("LoxPie", 3, 8f),
		("CookedMeat", 10, 15f),
		("CarrotSeeds", 10, 15f),
		("TurnipSeeds", 10, 15f),
		("OnionSeeds", 10, 12f),
		("BarleySeed", 10, 10f),
		("FlaxSeed", 10, 10f),
		("MeadHealthMedium", 3, 10f),
		("MeadStaminaMedium", 3, 10f),
		("MeadPoisonResist", 2, 8f),
		("AxeIron", 1, 3f),
		("AxeBlackMetal", 1, 2f),
		("BattleaxeBlackmetal", 1, 1f),
		("PickaxeBronze", 1, 3f),
		("PickaxeIron", 1, 2f),
		("SwordBronze", 1, 3f),
		("SwordIron", 1, 2f),
		("SwordBlackmetal", 1, 1f),
		("SwordMistwalker", 1, 0.5f),
		("BowFineWood", 1, 3f),
		("BowHuntsman", 1, 2f),
		("BowDraugrFang", 1, 1f),
		("APPLY_VITALITY", 1, 2f),
		("APPLY_ENDURANCE", 1, 2f),
		("APPLY_BURDEN", 1, 2f)
	};

	public static IEnumerable<string> GetLootPrefabNames()
	{
		foreach (var entry in LootTable)
		{
			if (!entry.item.StartsWith("APPLY_"))
			{
				yield return entry.item;
			}
		}
	}

	public static bool CanGiveItem(Player player, string prefabName, int amount, out string error)
	{
		error = null;
		if ((Object)(object)player == (Object)null)
		{
			error = "No player";
			return false;
		}
		GameObject val = ResolvePrefab(prefabName);
		if ((Object)(object)val == (Object)null)
		{
			error = "'" + prefabName + "' no longer exists in this version of Valheim";
			ManualLogSource log = Shopkeep.Log;
			if (log != null)
			{
				log.LogError((object)("Unresolved prefab: " + prefabName));
			}
			return false;
		}
		if ((Object)(object)val.GetComponent<ItemDrop>() == (Object)null)
		{
			error = "'" + prefabName + "' is not an item";
			return false;
		}
		if (!((Humanoid)player).GetInventory().CanAddItem(val, amount))
		{
			error = "Not enough inventory space!";
			return false;
		}
		return true;
	}

	public static bool TryGiveItem(Player player, string prefabName, int amount)
	{
		GameObject val = ResolvePrefab(prefabName);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		ItemDrop component = val.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null)
		{
			return false;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		int num = Mathf.Max(1, component.m_itemData.m_shared.m_maxStackSize);
		int num2 = amount;
		while (num2 > 0)
		{
			int num3 = Mathf.Min(num2, num);
			ItemData val2 = component.m_itemData.Clone();
			val2.m_stack = num3;
			val2.m_dropPrefab = val;
			if (!inventory.AddItem(val2))
			{
				ManualLogSource log = Shopkeep.Log;
				if (log != null)
				{
					log.LogWarning((object)$"Inventory rejected {prefabName} x{num3}");
				}
				return num2 < amount;
			}
			num2 -= num3;
		}
		return true;
	}

	private static GameObject ResolvePrefab(string prefabName)
	{
		GameObject val = null;
		if (PrefabManager.Instance != null)
		{
			val = PrefabManager.Instance.GetPrefab(prefabName);
		}
		if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
		{
			val = ZNetScene.instance.GetPrefab(prefabName);
		}
		return val;
	}

	public static void ApplyMysteryBox(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		float num = 0f;
		foreach (var item in LootTable)
		{
			num += item.chance;
		}
		float num2 = Random.Range(0f, num);
		float num3 = 0f;
		foreach (var item2 in LootTable)
		{
			num3 += item2.chance;
			if (num2 > num3)
			{
				continue;
			}
			switch (item2.item)
			{
			case "APPLY_VITALITY":
				ApplyScroll(player, "Shopkeep_Vitality", "Vitality", fromBox: true);
				break;
			case "APPLY_ENDURANCE":
				ApplyScroll(player, "Shopkeep_Endurance", "Endurance", fromBox: true);
				break;
			case "APPLY_BURDEN":
				ApplyScroll(player, "Shopkeep_Burden", "Burden", fromBox: true);
				break;
			default:
			{
				if (!CanGiveItem(player, item2.item, item2.amount, out var error))
				{
					ManualLogSource log = Shopkeep.Log;
					if (log != null)
					{
						log.LogWarning((object)("Mystery Box fallback (" + item2.item + "): " + error));
					}
					if (TryGiveItem(player, "Coins", 250))
					{
						Message("Mystery Box: 250 coins!");
					}
					else
					{
						Message("Mystery Box: no room in your inventory!");
					}
				}
				else
				{
					TryGiveItem(player, item2.item, item2.amount);
					Message($"Mystery Box: You found {item2.item} x{item2.amount}!");
				}
				break;
			}
			}
			break;
		}
	}

	public static bool IsScroll(string itemName, out string key, out string label)
	{
		switch (itemName)
		{
		case "ScrollOfVitality":
			key = "Shopkeep_Vitality";
			label = "Vitality";
			return true;
		case "ScrollOfEndurance":
			key = "Shopkeep_Endurance";
			label = "Endurance";
			return true;
		case "ScrollOfBurden":
			key = "Shopkeep_Burden";
			label = "Burden";
			return true;
		default:
			key = null;
			label = null;
			return false;
		}
	}

	public static bool CanApplyScroll(Player player, string key)
	{
		return (Object)(object)player != (Object)null && GetScrollUses(player, key) < 3;
	}

	public static void ApplyScrollOfVitality(Player player)
	{
		ApplyScroll(player, "Shopkeep_Vitality", "Vitality", fromBox: false);
	}

	public static void ApplyScrollOfEndurance(Player player)
	{
		ApplyScroll(player, "Shopkeep_Endurance", "Endurance", fromBox: false);
	}

	public static void ApplyScrollOfBurden(Player player)
	{
		ApplyScroll(player, "Shopkeep_Burden", "Burden", fromBox: false);
	}

	private static void ApplyScroll(Player player, string key, string label, bool fromBox)
	{
		if (!((Object)(object)player == (Object)null))
		{
			int scrollUses = GetScrollUses(player, key);
			if (scrollUses >= 3)
			{
				Message("You have already reached the limit for Scroll of " + label + "!");
				return;
			}
			scrollUses++;
			player.m_customData[key] = scrollUses.ToString();
			RefreshFoodStats(player);
			string text = (fromBox ? ("Mystery Box: You found a Scroll of " + label + "! ") : "");
			string text2 = ((key == "Shopkeep_Vitality") ? $"Max health +{25f}" : ((!(key == "Shopkeep_Endurance")) ? $"Carry weight +{100f}" : $"Max stamina +{25f}"));
			Message($"{text}{text2} ({scrollUses}/{3})");
		}
	}

	public static int GetScrollUses(Player player, string key)
	{
		if ((Object)(object)player == (Object)null || player.m_customData == null)
		{
			return 0;
		}
		if (!player.m_customData.TryGetValue(key, out var value))
		{
			return 0;
		}
		int result;
		return int.TryParse(value, out result) ? result : 0;
	}

	private static void RefreshFoodStats(Player player)
	{
		try
		{
			AccessTools.Method(typeof(Player), "UpdateFood", (Type[])null, (Type[])null)?.Invoke(player, new object[2] { 0f, true });
		}
		catch
		{
		}
	}

	private static void Message(string text)
	{
		if ((Object)(object)MessageHud.instance != (Object)null)
		{
			MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
		}
	}
}
[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
public static class Patch_Player_GetTotalFoodValue
{
	private static bool Prepare()
	{
		bool flag = AccessTools.Method(typeof(Player), "GetTotalFoodValue", (Type[])null, (Type[])null) != null;
		if (!flag)
		{
			ManualLogSource log = Shopkeep.Log;
			if (log != null)
			{
				log.LogError((object)"Player.GetTotalFoodValue not found - Vitality and Endurance scrolls disabled.");
			}
		}
		return flag;
	}

	private static void Postfix(Player __instance, ref float hp, ref float stamina)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			hp += (float)ItemEffects.GetScrollUses(__instance, "Shopkeep_Vitality") * 25f;
			stamina += (float)ItemEffects.GetScrollUses(__instance, "Shopkeep_Endurance") * 25f;
		}
	}
}
[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
public static class Patch_Player_GetMaxCarryWeight
{
	private static bool Prepare()
	{
		bool flag = AccessTools.Method(typeof(Player), "GetMaxCarryWeight", (Type[])null, (Type[])null) != null;
		if (!flag)
		{
			ManualLogSource log = Shopkeep.Log;
			if (log != null)
			{
				log.LogError((object)"Player.GetMaxCarryWeight not found - Burden scroll disabled.");
			}
		}
		return flag;
	}

	private static void Postfix(Player __instance, ref float __result)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			__result += (float)ItemEffects.GetScrollUses(__instance, "Shopkeep_Burden") * 100f;
		}
	}
}
public class ShopkeepInteract : MonoBehaviour, Interactable, Hoverable
{
	public bool Interact(Humanoid user, bool hold, bool alt)
	{
		if (hold)
		{
			return false;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null)
		{
			ManualLogSource log = Shopkeep.Log;
			if (log != null)
			{
				log.LogWarning((object)"Interact: user was not a Player.");
			}
			return false;
		}
		ShopUI.Show();
		return true;
	}

	public bool UseItem(Humanoid user, ItemData item)
	{
		return false;
	}

	public string GetHoverText()
	{
		return "Bercans\n[E] Browse Wares";
	}

	public string GetHoverName()
	{
		return "Bercans";
	}

	public float GetHoverOffset()
	{
		return 0f;
	}
}
public class ShopkeepNPC
{
	private const string BasePrefab = "piece_bone_throne";

	private const string VisualPrefab = "Haldor";

	public static void Register()
	{
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Expected O, but got Unknown
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Expected O, but got Unknown
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Expected O, but got Unknown
		GameObject val = PrefabManager.Instance.CreateClonedPrefab("Bercans", "piece_bone_throne");
		if ((Object)(object)val == (Object)null)
		{
			ManualLogSource log = Shopkeep.Log;
			if (log != null)
			{
				log.LogError((object)"Failed to clone 'piece_bone_throne' - Bercans will not be available. The base prefab may have been renamed or removed.");
			}
			return;
		}
		Interactable[] componentsInChildren = val.GetComponentsInChildren<Interactable>(true);
		foreach (Interactable val2 in componentsInChildren)
		{
			Object.DestroyImmediate((Object)(object)((val2 is Object) ? val2 : null), true);
		}
		Hoverable[] componentsInChildren2 = val.GetComponentsInChildren<Hoverable>(true);
		foreach (Hoverable val3 in componentsInChildren2)
		{
			Object.DestroyImmediate((Object)(object)((val3 is Object) ? val3 : null), true);
		}
		GameObject prefab = PrefabManager.Instance.GetPrefab("Haldor");
		if ((Object)(object)prefab == (Object)null)
		{
			ManualLogSource log2 = Shopkeep.Log;
			if (log2 != null)
			{
				log2.LogError((object)"Could not find 'Haldor' prefab. Bercans will register but have no visible model.");
			}
		}
		else
		{
			GameObject val4 = Object.Instantiate<GameObject>(prefab, val.transform);
			((Object)val4).name = "BercansVisual";
			val4.transform.localPosition = new Vector3(0f, 0.3f, 0.3f);
			val4.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
			val4.transform.localScale = Vector3.one;
			StripVisual(val4);
		}
		ShopkeepInteract shopkeepInteract = val.AddComponent<ShopkeepInteract>();
		if ((Object)(object)shopkeepInteract == (Object)null)
		{
			ManualLogSource log3 = Shopkeep.Log;
			if (log3 != null)
			{
				log3.LogError((object)"Failed to add ShopkeepInteract component!");
			}
		}
		PieceConfig val5 = new PieceConfig();
		val5.Name = "Bercans";
		val5.Description = "A traveling merchant with rare wares.";
		val5.PieceTable = "Hammer";
		val5.AddRequirement(new RequirementConfig("Coins", 1000, 0, true));
		PieceManager.Instance.AddPiece(new CustomPiece(val, false, val5));
		DumpPrefabDiagnostics(val);
		ManualLogSource log4 = Shopkeep.Log;
		if (log4 != null)
		{
			log4.LogInfo((object)"Bercans registered.");
		}
	}

	private static void StripVisual(GameObject visual)
	{
		int num = 0;
		Collider[] componentsInChildren = visual.GetComponentsInChildren<Collider>(true);
		foreach (Collider val in componentsInChildren)
		{
			Object.DestroyImmediate((Object)(object)val, true);
			num++;
		}
		Interactable[] componentsInChildren2 = visual.GetComponentsInChildren<Interactable>(true);
		foreach (Interactable val2 in componentsInChildren2)
		{
			Object.DestroyImmediate((Object)(object)((val2 is Object) ? val2 : null), true);
		}
		Hoverable[] componentsInChildren3 = visual.GetComponentsInChildren<Hoverable>(true);
		foreach (Hoverable val3 in componentsInChildren3)
		{
			Object.DestroyImmediate((Object)(object)((val3 is Object) ? val3 : null), true);
		}
		Trader component = visual.GetComponent<Trader>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component, true);
		}
		Character component2 = visual.GetComponent<Character>();
		if ((Object)(object)component2 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component2, true);
		}
		BaseAI component3 = visual.GetComponent<BaseAI>();
		if ((Object)(object)component3 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component3, true);
		}
		ZNetView component4 = visual.GetComponent<ZNetView>();
		if ((Object)(object)component4 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component4, true);
		}
		ZSyncTransform component5 = visual.GetComponent<ZSyncTransform>();
		if ((Object)(object)component5 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component5, true);
		}
		Rigidbody component6 = visual.GetComponent<Rigidbody>();
		if ((Object)(object)component6 != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component6, true);
		}
		MonoBehaviour[] componentsInChildren4 = visual.GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val4 in componentsInChildren4)
		{
			if (!((Object)(object)val4 == (Object)null))
			{
				((Behaviour)val4).enabled = false;
			}
		}
		ManualLogSource log = Shopkeep.Log;
		if (log != null)
		{
			log.LogInfo((object)$"Visual stripped: removed {num} collider(s).");
		}
	}

	private static void DumpPrefabDiagnostics(GameObject prefab)
	{
		ManualLogSource log = Shopkeep.Log;
		if (log != null)
		{
			log.LogInfo((object)"--- Bercans prefab diagnostics ---");
		}
		ManualLogSource log2 = Shopkeep.Log;
		if (log2 != null)
		{
			log2.LogInfo((object)$"  Root layer: {prefab.layer} ({LayerMask.LayerToName(prefab.layer)})");
		}
		ManualLogSource log3 = Shopkeep.Log;
		if (log3 != null)
		{
			log3.LogInfo((object)$"  ShopkeepInteract present: {(Object)(object)prefab.GetComponent<ShopkeepInteract>() != (Object)null}");
		}
		ManualLogSource log4 = Shopkeep.Log;
		if (log4 != null)
		{
			log4.LogInfo((object)$"  Piece present: {(Object)(object)prefab.GetComponent<Piece>() != (Object)null}");
		}
		ManualLogSource log5 = Shopkeep.Log;
		if (log5 != null)
		{
			log5.LogInfo((object)$"  ZNetView present: {(Object)(object)prefab.GetComponent<ZNetView>() != (Object)null}");
		}
		Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
		ManualLogSource log6 = Shopkeep.Log;
		if (log6 != null)
		{
			log6.LogInfo((object)$"  Colliders: {componentsInChildren.Length}");
		}
		Collider[] array = componentsInChildren;
		foreach (Collider val in array)
		{
			ManualLogSource log7 = Shopkeep.Log;
			if (log7 != null)
			{
				log7.LogInfo((object)("    " + ((Object)((Component)val).gameObject).name + " layer=" + LayerMask.LayerToName(((Component)val).gameObject.layer) + " " + $"trigger={val.isTrigger} " + $"enabled={val.enabled}"));
			}
		}
		Hoverable[] componentsInChildren2 = prefab.GetComponentsInChildren<Hoverable>(true);
		ManualLogSource log8 = Shopkeep.Log;
		if (log8 != null)
		{
			log8.LogInfo((object)$"  Hoverables: {componentsInChildren2.Length}");
		}
		Hoverable[] array2 = componentsInChildren2;
		foreach (Hoverable val2 in array2)
		{
			ManualLogSource log9 = Shopkeep.Log;
			if (log9 != null)
			{
				log9.LogInfo((object)("    " + ((object)val2).GetType().Name));
			}
		}
		ManualLogSource log10 = Shopkeep.Log;
		if (log10 != null)
		{
			log10.LogInfo((object)"--- end diagnostics ---");
		}
	}
}
public class ShopKeyListener : MonoBehaviour
{
	private static bool warnedAboutJoyBinding;

	private void Update()
	{
		if (Input.GetKeyDown((KeyCode)27))
		{
			ShopUI.Hide();
			return;
		}
		try
		{
			if (ZInput.instance != null && ZInput.GetButtonDown("JoyButtonB"))
			{
				ShopUI.Hide();
			}
		}
		catch (Exception ex)
		{
			if (!warnedAboutJoyBinding)
			{
				warnedAboutJoyBinding = true;
				ManualLogSource log = Shopkeep.Log;
				if (log != null)
				{
					log.LogWarning((object)("Controller close binding unavailable: " + ex.Message));
				}
			}
		}
	}
}
public class ShopUI
{
	private struct ShopItem
	{
		public string itemName;

		public string displayName;

		public int price;

		public int amount;

		public string category;
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__9_0;

		internal void <CreateUI>b__9_0()
		{
			Hide();
		}
	}

	private static GameObject shopPanel;

	private static GameObject coinsLabel;

	private static bool isOpen = false;

	private static List<ShopItem> shopItems = new List<ShopItem>
	{
		new ShopItem
		{
			itemName = "CookedMeat",
			displayName = "Cooked Meat",
			price = 50,
			amount = 5,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "Bread",
			displayName = "Bread",
			price = 8,
			amount = 5,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "FishCooked",
			displayName = "Cooked Fish",
			price = 60,
			amount = 5,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "SerpentStew",
			displayName = "Serpent Stew",
			price = 250,
			amount = 3,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "LoxPie",
			displayName = "Lox Meat Pie",
			price = 300,
			amount = 3,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "BloodPudding",
			displayName = "Blood Pudding",
			price = 200,
			amount = 3,
			category = "Food"
		},
		new ShopItem
		{
			itemName = "MeadHealthMinor",
			displayName = "Minor Health Mead",
			price = 150,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MeadHealthMedium",
			displayName = "Medium Health Mead",
			price = 250,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MeadStaminaMinor",
			displayName = "Minor Stamina Mead",
			price = 150,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MeadStaminaMedium",
			displayName = "Medium Stamina Mead",
			price = 250,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MeadPoisonResist",
			displayName = "Poison Resistance Mead",
			price = 300,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MeadFrostResist",
			displayName = "Frost Resistance Mead",
			price = 300,
			amount = 1,
			category = "Potions"
		},
		new ShopItem
		{
			itemName = "MysteryBox",
			displayName = "Mystery Box",
			price = 500,
			amount = 1,
			category = "Special"
		},
		new ShopItem
		{
			itemName = "ScrollOfVitality",
			displayName = "Scroll of Vitality (+25 Health)",
			price = 2000,
			amount = 1,
			category = "Special"
		},
		new ShopItem
		{
			itemName = "ScrollOfEndurance",
			displayName = "Scroll of Endurance (+25 Stamina)",
			price = 2000,
			amount = 1,
			category = "Special"
		},
		new ShopItem
		{
			itemName = "ScrollOfBurden",
			displayName = "Scroll of Burden (+100 Carry Weight)",
			price = 2000,
			amount = 1,
			category = "Special"
		}
	};

	public static IEnumerable<string> GetPrefabNames()
	{
		foreach (ShopItem item in shopItems)
		{
			if (!IsSpecial(item.itemName))
			{
				yield return item.itemName;
			}
		}
	}

	private static bool IsSpecial(string itemName)
	{
		string key;
		string label;
		return itemName == "MysteryBox" || ItemEffects.IsScroll(itemName, out key, out label);
	}

	public static void Show()
	{
		if (isOpen)
		{
			Hide();
			return;
		}
		CreateUI();
		isOpen = true;
		GUIManager.BlockInput(true);
	}

	public static void Hide()
	{
		if ((Object)(object)shopPanel != (Object)null)
		{
			Object.Destroy((Object)(object)shopPanel);
			shopPanel = null;
		}
		coinsLabel = null;
		isOpen = false;
		GUIManager.BlockInput(false);
	}

	private static void CreateUI()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d3: Expected O, but got Unknown
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Expected O, but got Unknown
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Expected O, but got Unknown
		//IL_0317: Unknown result type (might be due to invalid IL or missing references)
		//IL_032e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0345: Unknown result type (might be due to invalid IL or missing references)
		//IL_0352: Unknown result type (might be due to invalid IL or missing references)
		//IL_035f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_0390: Expected O, but got Unknown
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Expected O, but got Unknown
		//IL_0435: Unknown result type (might be due to invalid IL or missing references)
		//IL_0444: Unknown result type (might be due to invalid IL or missing references)
		//IL_0453: Unknown result type (might be due to invalid IL or missing references)
		//IL_0469: Unknown result type (might be due to invalid IL or missing references)
		//IL_046f: Unknown result type (might be due to invalid IL or missing references)
		shopPanel = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-400f, 0f), 1400f, 1500f, true);
		shopPanel.AddComponent<ShopKeyListener>();
		GUIManager.Instance.CreateText("Free Gold Market", shopPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -60f), GUIManager.Instance.AveriaSerifBold, 36, GUIManager.Instance.ValheimOrange, true, Color.black, 1200f, 60f, false);
		coinsLabel = GUIManager.Instance.CreateText($"Your Coins: {GetPlayerCoins()}", shopPanel.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -110f), GUIManager.Instance.AveriaSerifBold, 24, Color.yellow, true, Color.black, 1200f, 40f, false);
		GameObject val = GUIManager.Instance.CreateButton("X", shopPanel.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-25f, -25f), 40f, 40f);
		ButtonClickedEvent onClick = val.GetComponent<Button>().onClick;
		object obj = <>c.<>9__9_0;
		if (obj == null)
		{
			UnityAction val2 = delegate
			{
				Hide();
			};
			<>c.<>9__9_0 = val2;
			obj = (object)val2;
		}
		((UnityEvent)onClick).AddListener((UnityAction)obj);
		GameObject val3 = new GameObject("ScrollView");
		val3.transform.SetParent(shopPanel.transform, false);
		RectTransform val4 = val3.AddComponent<RectTransform>();
		val4.anchorMin = new Vector2(0.5f, 0f);
		val4.anchorMax = new Vector2(0.5f, 1f);
		val4.offsetMin = new Vector2(-670f, 10f);
		val4.offsetMax = new Vector2(670f, -160f);
		ScrollRect val5 = val3.AddComponent<ScrollRect>();
		GameObject val6 = new GameObject("Viewport");
		val6.transform.SetParent(val3.transform, false);
		RectTransform val7 = val6.AddComponent<RectTransform>();
		val7.anchorMin = Vector2.zero;
		val7.anchorMax = Vector2.one;
		val7.offsetMin = Vector2.zero;
		val7.offsetMax = Vector2.zero;
		val6.AddComponent<Mask>().showMaskGraphic = false;
		((Graphic)val6.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.1f);
		GameObject val8 = new GameObject("Content");
		val8.transform.SetParent(val6.transform, false);
		RectTransform val9 = val8.AddComponent<RectTransform>();
		val9.anchorMin = new Vector2(0f, 1f);
		val9.anchorMax = new Vector2(1f, 1f);
		val9.pivot = new Vector2(0.5f, 1f);
		val9.offsetMin = Vector2.zero;
		val9.offsetMax = Vector2.zero;
		VerticalLayoutGroup val10 = val8.AddComponent<VerticalLayoutGroup>();
		((HorizontalOrVerticalLayoutGroup)val10).spacing = 15f;
		((LayoutGroup)val10).padding = new RectOffset(5, 5, 5, 5);
		ContentSizeFitter val11 = val8.AddComponent<ContentSizeFitter>();
		val11.verticalFit = (FitMode)2;
		val5.viewport = val7;
		val5.content = val9;
		val5.horizontal = false;
		val5.vertical = true;
		string text = "";
		foreach (ShopItem shopItem in shopItems)
		{
			if (shopItem.category != text)
			{
				text = shopItem.category;
				GUIManager.Instance.CreateText("-- " + shopItem.category + " --", val8.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(10f, 0f), GUIManager.Instance.AveriaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 1200f, 40f, false);
			}
			CreateItemRow(val8.transform, shopItem);
		}
	}

	private static void CreateItemRow(Transform parent, ShopItem item)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Expected O, but got Unknown
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0212: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0287: Unknown result type (might be due to invalid IL or missing references)
		//IL_0291: Expected O, but got Unknown
		GameObject val = new GameObject("Row_" + item.itemName);
		val.transform.SetParent(parent, false);
		RectTransform val2 = val.AddComponent<RectTransform>();
		val2.sizeDelta = new Vector2(1300f, 60f);
		HorizontalLayoutGroup val3 = val.AddComponent<HorizontalLayoutGroup>();
		((HorizontalOrVerticalLayoutGroup)val3).spacing = 0f;
		((LayoutGroup)val3).childAlignment = (TextAnchor)3;
		((LayoutGroup)val3).padding = new RectOffset(10, 10, 2, 2);
		((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false;
		((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = false;
		GameObject val4 = GUIManager.Instance.CreateText($"{item.displayName} x{item.amount}", val.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, GUIManager.Instance.AveriaSerifBold, 22, Color.white, true, Color.black, 850f, 50f, false);
		LayoutElement val5 = val4.AddComponent<LayoutElement>();
		val5.minWidth = 850f;
		val5.preferredWidth = 850f;
		val5.minHeight = 50f;
		val5.preferredHeight = 50f;
		GameObject val6 = GUIManager.Instance.CreateText($"{item.price} coins", val.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, GUIManager.Instance.AveriaSerifBold, 22, Color.yellow, true, Color.black, 250f, 50f, false);
		LayoutElement val7 = val6.AddComponent<LayoutElement>();
		val7.minWidth = 250f;
		val7.preferredWidth = 250f;
		val7.minHeight = 50f;
		val7.preferredHeight = 50f;
		ShopItem capturedItem = item;
		GameObject val8 = GUIManager.Instance.CreateButton("Buy", val.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, 120f, 55f);
		StyleLabel(val8, Color.white, 20);
		LayoutElement val9 = val8.AddComponent<LayoutElement>();
		val9.minWidth = 120f;
		val9.preferredWidth = 120f;
		val9.minHeight = 55f;
		val9.preferredHeight = 55f;
		((UnityEvent)val8.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
		{
			BuyItem(capturedItem);
		});
	}

	private static void StyleLabel(GameObject go, Color color, int fontSize)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		TMP_Text componentInChildren = go.GetComponentInChildren<TMP_Text>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			((Graphic)componentInChildren).color = color;
			componentInChildren.fontSize = fontSize;
			return;
		}
		Text componentInChildren2 = go.GetComponentInChildren<Text>(true);
		if ((Object)(object)componentInChildren2 != (Object)null)
		{
			((Graphic)componentInChildren2).color = color;
			componentInChildren2.fontSize = fontSize;
		}
	}

	private static void SetLabel(GameObject go, string text)
	{
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		TMP_Text componentInChildren = go.GetComponentInChildren<TMP_Text>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.text = text;
			return;
		}
		Text componentInChildren2 = go.GetComponentInChildren<Text>(true);
		if ((Object)(object)componentInChildren2 != (Object)null)
		{
			componentInChildren2.text = text;
		}
	}

	private static void RefreshCoins()
	{
		SetLabel(coinsLabel, $"Your Coins: {GetPlayerCoins()}");
	}

	private static void BuyItem(ShopItem item)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		bool flag = item.itemName == "MysteryBox";
		string key;
		string label;
		bool flag2 = ItemEffects.IsScroll(item.itemName, out key, out label);
		if (flag2 && !ItemEffects.CanApplyScroll(localPlayer, key))
		{
			Message("You have already reached the limit for Scroll of " + label + "!");
			return;
		}
		if (!flag && !flag2 && !ItemEffects.CanGiveItem(localPlayer, item.itemName, item.amount, out var error))
		{
			Message("Cannot buy " + item.displayName + ": " + error);
			return;
		}
		int playerCoins = GetPlayerCoins();
		if (playerCoins < item.price)
		{
			Message("Not enough coins!");
			return;
		}
		if (!RemoveCoins(localPlayer, item.price))
		{
			Message("Payment failed - nothing was charged.");
			return;
		}
		if (flag)
		{
			ItemEffects.ApplyMysteryBox(localPlayer);
		}
		else if (flag2)
		{
			if (key == "Shopkeep_Vitality")
			{
				ItemEffects.ApplyScrollOfVitality(localPlayer);
			}
			else if (key == "Shopkeep_Endurance")
			{
				ItemEffects.ApplyScrollOfEndurance(localPlayer);
			}
			else
			{
				ItemEffects.ApplyScrollOfBurden(localPlayer);
			}
		}
		else
		{
			ItemEffects.TryGiveItem(localPlayer, item.itemName, item.amount);
			Message("Purchased " + item.displayName + "!");
		}
		RefreshCoins();
	}

	private static bool RemoveCoins(Player player, int price)
	{
		Inventory inventory = ((Humanoid)player).GetInventory();
		int num = price;
		List<ItemData> list = new List<ItemData>();
		foreach (ItemData allItem in inventory.GetAllItems())
		{
			if (num <= 0)
			{
				break;
			}
			if (IsCoins(allItem))
			{
				if (num >= allItem.m_stack)
				{
					num -= allItem.m_stack;
					list.Add(allItem);
				}
				else
				{
					allItem.m_stack -= num;
					num = 0;
				}
			}
		}
		if (num > 0)
		{
			return false;
		}
		foreach (ItemData item in list)
		{
			inventory.RemoveItem(item);
		}
		return true;
	}

	private static bool IsCoins(ItemData item)
	{
		if (item == null)
		{
			return false;
		}
		if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "Coins")
		{
			return true;
		}
		if (item.m_shared != null && item.m_shared.m_name == "$item_coins")
		{
			return true;
		}
		return false;
	}

	private static int GetPlayerCoins()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return 0;
		}
		int num = 0;
		foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems())
		{
			if (IsCoins(allItem))
			{
				num += allItem.m_stack;
			}
		}
		return num;
	}

	private static void Message(string text)
	{
		if ((Object)(object)MessageHud.instance != (Object)null)
		{
			MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
		}
	}
}