Decompiled source of ShiftingWares v1.0.0

plugins/ShiftingWares/ShiftingWares.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ShiftingWares;

[BepInPlugin("felih.ShiftingWares", "Shifting Wares", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "felih.ShiftingWares";

	public const string NAME = "Shifting Wares";

	public const string VERSION = "1.0.0";

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<int> RotationDays;

	internal static ConfigEntry<float> PriceMultiplier;

	internal static ConfigEntry<bool> ShowCountdown;

	internal static ConfigEntry<bool> DetailedLog;

	private void Awake()
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Expected O, but got Unknown
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Expected O, but got Unknown
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Haldor brings a few travel wares on top of his usual goods, and they change every few days.");
		RotationDays = ((BaseUnityPlugin)this).Config.Bind<int>("1 - General", "RotationDays", 3, new ConfigDescription("In-game days between new travel wares.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), new object[0]));
		PriceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "PriceMultiplier", 1f, new ConfigDescription("Scales the price of the travel wares.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), new object[0]));
		ShowCountdown = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowCountdown", true, "Show the days left until new travel wares when you look at Haldor.");
		DetailedLog = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Debug", "DetailedLog", false, "Writes the chosen wares to the BepInEx log.");
		((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("1 - General", "ShownPercent"));
		((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("1 - General", "MinShown"));
		new Harmony("felih.ShiftingWares").PatchAll(typeof(Plugin).Assembly);
		Log.LogInfo((object)"Shifting Wares 1.0.0 loaded");
	}
}
internal class Stage
{
	internal string key;

	internal int unit;

	internal int stack;

	internal string[] items;

	internal Stage(string key, int unit, int stack, params string[] items)
	{
		this.key = key;
		this.unit = unit;
		this.stack = stack;
		this.items = items;
	}
}
internal static class Wares
{
	private const int MinWares = 3;

	private const int MaxWares = 5;

	private const float GearChance = 0.5f;

	private static readonly Stage[] Stages = new Stage[7]
	{
		new Stage(null, 2, 20, "Feathers", "LeatherScraps", "Resin", "Honey", "Raspberry", "Flint", "DeerHide", "NeckTail"),
		new Stage("defeated_eikthyr", 3, 15, "TrollHide", "Blueberries", "MushroomYellow", "CarrotSeeds", "Thistle", "GreydwarfEye", "FineWood", "RoundLog"),
		new Stage("defeated_gdking", 4, 10, "Guck", "Root", "Entrails", "Bloodbag", "TurnipSeeds", "Chitin", "ElderBark", "Ooze"),
		new Stage("defeated_bonemass", 6, 10, "WolfPelt", "Obsidian", "Crystal", "WolfFang", "OnionSeeds", "FreezeGland", "JuteRed"),
		new Stage("defeated_dragon", 8, 10, "Needle", "Barley", "Flax", "Tar", "Cloudberry", "LoxPelt"),
		new Stage("defeated_goblinking", 12, 5, "Carapace", "SoftTissue", "RoyalJelly", "Sap", "MushroomJotunPuffs", "MushroomMagecap", "Mandible", "ScaleHide"),
		new Stage("defeated_queen", 16, 5, "CharredBone", "AskHide", "Grausten", "MushroomSmokePuff", "Vineberry", "FiddleheadFern", "VoltureFeather")
	};

	private static readonly int[] GearPrice = new int[7] { 400, 400, 700, 1100, 1600, 2200, 3000 };

	private static readonly string[] GearExtra = new string[4] { "SledgeDemolisher", "MaceSilver", "SpearChitin", "BowDraugrFang" };

	private static readonly string[][] StageMats = new string[7][]
	{
		new string[0],
		new string[5] { "Copper", "Tin", "Bronze", "CopperOre", "TinOre" },
		new string[3] { "Iron", "IronScrap", "IronOre" },
		new string[3] { "Silver", "SilverOre", "DragonEgg" },
		new string[3] { "BlackMetal", "BlackMetalScrap", "DragonTear" },
		new string[6] { "Eitr", "YggdrasilWood", "BlackCore", "Wisp", "DvergrNeedle", "Copper_Mistlands" },
		new string[6] { "Flametal", "FlametalNew", "FlametalOre", "FlametalOreNew", "CelestialFeather", "MoltenCore" }
	};

	private static Dictionary<string, int> _matStage;

	private static List<KeyValuePair<ItemDrop, int>> _gear;

	private static int _cachePeriod = -1;

	private static string _cacheKeys;

	private static readonly List<TradeItem> _cache = new List<TradeItem>();

	private static readonly HashSet<string> _missingLogged = new HashSet<string>();

	internal static bool IsHaldor(Trader t)
	{
		if (!Plugin.Enabled.Value || (Object)(object)t == (Object)null)
		{
			return false;
		}
		if (!Low(t.m_name).Contains("haldor"))
		{
			return Low(Utils.GetPrefabName(((Component)t).gameObject)).Contains("haldor");
		}
		return true;
	}

	private static string Low(string s)
	{
		if (!string.IsNullOrEmpty(s))
		{
			return s.ToLowerInvariant();
		}
		return "";
	}

	internal static int Day()
	{
		if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null)
		{
			return 0;
		}
		return EnvMan.instance.GetDay();
	}

	internal static int Period()
	{
		return Day() / Mathf.Max(1, Plugin.RotationDays.Value);
	}

	internal static int DaysLeft()
	{
		int num = Mathf.Max(1, Plugin.RotationDays.Value);
		return num - Day() % num;
	}

	private static bool Open(Stage s)
	{
		if (s.key != null)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(s.key);
			}
			return false;
		}
		return true;
	}

	internal static List<TradeItem> Current(Trader t)
	{
		int num = Period();
		string text = "";
		for (int i = 0; i < Stages.Length; i++)
		{
			text += (Open(Stages[i]) ? "1" : "0");
		}
		if (num == _cachePeriod && text == _cacheKeys)
		{
			return _cache;
		}
		_cachePeriod = num;
		_cacheKeys = text;
		_cache.Clear();
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			_cachePeriod = -1;
			return _cache;
		}
		HashSet<string> hashSet = new HashSet<string>();
		foreach (TradeItem item in t.m_items)
		{
			if ((Object)(object)item.m_prefab != (Object)null)
			{
				hashSet.Add(((Object)((Component)item.m_prefab).gameObject).name);
			}
		}
		int num2 = -1;
		for (int j = 0; j < Stages.Length; j++)
		{
			if (Open(Stages[j]))
			{
				num2 = j;
			}
		}
		List<KeyValuePair<TradeItem, int>> list = new List<KeyValuePair<TradeItem, int>>();
		for (int k = 0; k <= num2; k++)
		{
			Stage stage = Stages[k];
			if (!Open(stage))
			{
				continue;
			}
			int value = ((k == num2) ? 4 : ((k != num2 - 1) ? 1 : 2));
			string[] items = stage.items;
			foreach (string text2 in items)
			{
				if (hashSet.Contains(text2))
				{
					continue;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text2);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val == (Object)null)
				{
					if (Plugin.DetailedLog.Value && _missingLogged.Add(text2))
					{
						Plugin.Log.LogInfo((object)("[wares] no item prefab " + text2));
					}
				}
				else
				{
					list.Add(new KeyValuePair<TradeItem, int>(Make(val, stage), value));
				}
			}
		}
		Random random = new Random(Seed() ^ (num * 7919) ^ 0x5A17);
		int num3 = random.Next(3, 6);
		while (_cache.Count < num3 && list.Count > 0)
		{
			int num4 = 0;
			foreach (KeyValuePair<TradeItem, int> item2 in list)
			{
				num4 += item2.Value;
			}
			int num5 = random.Next(num4);
			for (int m = 0; m < list.Count; m++)
			{
				num5 -= list[m].Value;
				if (num5 < 0)
				{
					_cache.Add(list[m].Key);
					list.RemoveAt(m);
					break;
				}
			}
		}
		if (random.NextDouble() < 0.5)
		{
			List<KeyValuePair<ItemDrop, int>> list2 = new List<KeyValuePair<ItemDrop, int>>();
			int num6 = 0;
			foreach (KeyValuePair<ItemDrop, int> item3 in Gear())
			{
				if (item3.Value <= num2 && !hashSet.Contains(((Object)((Component)item3.Key).gameObject).name))
				{
					list2.Add(item3);
					num6 += ((item3.Value == num2) ? 4 : ((item3.Value != num2 - 1) ? 1 : 2));
				}
			}
			if (list2.Count > 0)
			{
				int num7 = random.Next(num6);
				foreach (KeyValuePair<ItemDrop, int> item4 in list2)
				{
					num7 -= ((item4.Value == num2) ? 4 : ((item4.Value != num2 - 1) ? 1 : 2));
					if (num7 < 0)
					{
						TradeItem val2 = Make(item4.Key, Stages[item4.Value]);
						val2.m_stack = 1;
						val2.m_price = Mathf.Max(1, Mathf.RoundToInt((float)GearPrice[item4.Value] * Plugin.PriceMultiplier.Value));
						_cache.Add(val2);
						break;
					}
				}
			}
		}
		if (Plugin.DetailedLog.Value)
		{
			List<string> list3 = new List<string>();
			foreach (TradeItem item5 in _cache)
			{
				list3.Add(((Object)((Component)item5.m_prefab).gameObject).name + " x" + item5.m_stack + " " + item5.m_price);
			}
			Plugin.Log.LogInfo((object)("[wares] day " + Day() + " period " + num + " stages " + text + ": " + string.Join(", ", list3.ToArray())));
		}
		return _cache;
	}

	private static List<KeyValuePair<ItemDrop, int>> Gear()
	{
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: 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_012a: Invalid comparison between Unknown and I4
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Invalid comparison between Unknown and I4
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Invalid comparison between Unknown and I4
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Invalid comparison between Unknown and I4
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Invalid comparison between Unknown and I4
		if (_gear != null)
		{
			return _gear;
		}
		_gear = new List<KeyValuePair<ItemDrop, int>>();
		_matStage = new Dictionary<string, int>();
		for (int i = 0; i < Stages.Length; i++)
		{
			string[] items = Stages[i].items;
			foreach (string key in items)
			{
				_matStage[key] = i;
			}
		}
		for (int k = 0; k < StageMats.Length; k++)
		{
			string[] array = StageMats[k];
			foreach (string key2 in array)
			{
				_matStage[key2] = k;
			}
		}
		HashSet<string> hashSet = new HashSet<string>(GearExtra);
		HashSet<string> hashSet2 = new HashSet<string>();
		foreach (Recipe recipe in ObjectDB.instance.m_recipes)
		{
			if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || !recipe.m_enabled || recipe.m_resources == null)
			{
				continue;
			}
			ItemType itemType = recipe.m_item.m_itemData.m_shared.m_itemType;
			if ((int)itemType != 3 && (int)itemType != 14 && (int)itemType != 22 && (int)itemType != 4 && (int)itemType != 5)
			{
				continue;
			}
			string name = ((Object)((Component)recipe.m_item).gameObject).name;
			bool flag = false;
			int num = 0;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null))
				{
					string name2 = ((Object)((Component)val.m_resItem).gameObject).name;
					if (name2 == "YmirRemains")
					{
						flag = true;
					}
					if (_matStage.TryGetValue(name2, out var value) && value > num)
					{
						num = value;
					}
				}
			}
			if ((flag || hashSet.Contains(name)) && hashSet2.Add(name))
			{
				_gear.Add(new KeyValuePair<ItemDrop, int>(recipe.m_item, num));
			}
		}
		if (Plugin.DetailedLog.Value)
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<ItemDrop, int> item in _gear)
			{
				list.Add(((Object)((Component)item.Key).gameObject).name + "(" + item.Value + ")");
			}
			Plugin.Log.LogInfo((object)("[wares] gear pool: " + string.Join(", ", list.ToArray())));
		}
		return _gear;
	}

	private static TradeItem Make(ItemDrop drop, Stage s)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Expected O, but got Unknown
		SharedData shared = drop.m_itemData.m_shared;
		int num = Mathf.Clamp(s.stack, 1, Mathf.Max(1, shared.m_maxStackSize));
		int num2 = Mathf.Max(s.unit, shared.m_value * 2);
		int price = Mathf.Max(1, Mathf.RoundToInt((float)(num2 * num) * Plugin.PriceMultiplier.Value));
		TradeItem val = new TradeItem();
		val.m_prefab = drop;
		val.m_stack = num;
		val.m_price = price;
		val.m_requiredGlobalKey = "";
		val.m_buyKey = "";
		val.m_name = "";
		val.m_tooltip = "";
		val.m_buyPlayerEffects = new EffectList();
		return val;
	}

	private static int Seed()
	{
		if (WorldGenerator.instance != null)
		{
			return WorldGenerator.instance.GetSeed();
		}
		return 0;
	}
}
[HarmonyPatch(typeof(Trader), "GetAvailableItems")]
internal static class Trader_GetAvailableItems
{
	private static void Postfix(Trader __instance, List<TradeItem> __result)
	{
		try
		{
			if (__result != null && Wares.IsHaldor(__instance))
			{
				__result.AddRange(Wares.Current(__instance));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("GetAvailableItems: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(Trader), "GetHoverText")]
internal static class Trader_GetHoverText
{
	private static void Postfix(Trader __instance, ref string __result)
	{
		if (Plugin.ShowCountdown.Value && Wares.IsHaldor(__instance))
		{
			int num = Wares.DaysLeft();
			__result = __result + "\n<color=#bbbbbb>New travel wares " + ((num == 1) ? "tomorrow" : ("in " + num + " days")) + "</color>";
		}
	}
}
[HarmonyPatch(typeof(StoreGui), "Show")]
internal static class StoreGui_Show
{
	private static int _seen = -1;

	private static void Postfix(Trader trader)
	{
		if (Wares.IsHaldor(trader) && !((Object)(object)Player.m_localPlayer == (Object)null))
		{
			int num = Wares.Period();
			if (_seen >= 0 && _seen != num)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, Localization.instance.Localize(trader.m_name) + " brought new travel wares", 0, (Sprite)null, false);
			}
			_seen = num;
		}
	}
}