Decompiled source of MySix UniqueItems v1.0.3

MySix.UniqueItems.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using MySix.Core;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MySix
{
	internal static class SuiteVersion
	{
		public const string Value = "1.0.3";
	}
}
namespace MySix.UniqueItems
{
	public class UniqueItems
	{
		public const string FeatureName = "UniqueItems";

		private readonly Setting<string> _extraPrefabs;

		private readonly Setting<bool> _startClockForOldPicks;

		private readonly HashSet<int> _pickupPrefabHashes = new HashSet<int>();

		private readonly HashSet<string> _ordinaryPickups = new HashSet<string>();

		private readonly List<string> _locationNames = new List<string>();

		private int _clocksStarted;

		private bool _firstPassDone;

		public bool Applied { get; private set; }

		public IEnumerable<string> Pickups => UniqueTargets.Pickups;

		public IEnumerable<string> Spawners => UniqueTargets.Spawners;

		public int UniqueLocations => _locationNames.Count;

		public int ClocksStarted => _clocksStarted;

		public bool StartClockForOldPicks => _startClockForOldPicks.Value;

		public UniqueItems(ConfigFile config)
		{
			UniqueTargets.Enabled = Settings.Register<bool>("UniqueItems", config.Bind<bool>("UniqueItems", "Enabled", true, "One-of-a-kind pickups and creatures inside unique world locations come back after a while, so every player can have them."), (Scope)0, (string)null);
			UniqueTargets.Minutes = Settings.Register<float>("UniqueItems", config.Bind<float>("UniqueItems", "RespawnMinutes", 30f, "How long, in minutes of in-game time, before one of them comes back. One game day is 30 minutes."), (Scope)0, (string)null);
			UniqueTargets.CoverSpawners = Settings.Register<bool>("UniqueItems", config.Bind<bool>("UniqueItems", "CoverSpawners", true, "Also bring back the one-off creatures of those locations, such as Lord Reto, and not only the pickups."), (Scope)0, (string)null);
			_extraPrefabs = Settings.Register<string>("UniqueItems", config.Bind<string>("UniqueItems", "ExtraPrefabs", "", "Extra pickup or spawner names to treat as one-of-a-kind, separated by commas. Only needed for something the game's location list does not cover."), (Scope)0, (string)null);
			_startClockForOldPicks = Settings.Register<bool>("UniqueItems", config.Bind<bool>("UniqueItems", "StartClockForOldPicks", false, "On the machine that owns the world, start the respawn clock for pickups taken before this mod was installed, instead of waiting until a player next visits them. Costs one pass over the world."), (Scope)0, (string)null);
		}

		public void Apply()
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			UniqueTargets.Pickups.Clear();
			UniqueTargets.Spawners.Clear();
			_pickupPrefabHashes.Clear();
			_ordinaryPickups.Clear();
			_locationNames.Clear();
			_clocksStarted = 0;
			_firstPassDone = false;
			Applied = true;
			if (!UniqueTargets.Enabled.Value)
			{
				CorePlugin.Log.LogInfo((object)"UniqueItems: disabled");
				return;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			foreach (ZoneLocation location in ZoneSystem.instance.m_locations)
			{
				if (location.m_enable && location.m_prefab.IsValid && (location.m_unique || location.m_quantity == 1))
				{
					_locationNames.Add(location.m_prefabName + (location.m_unique ? "" : " (quantity 1)"));
					location.m_prefab.Load();
					GameObject asset = location.m_prefab.Asset;
					if ((Object)(object)asset == (Object)null)
					{
						CorePlugin.Log.LogWarning((object)("UniqueItems: could not load the prefab for location " + location.m_prefabName));
					}
					else
					{
						Collect(asset, location.m_prefabName);
					}
					location.m_prefab.Release();
				}
			}
			foreach (string item in from n in _extraPrefabs.Value.Split(new char[1] { ',' })
				select n.Trim() into n
				where n.Length > 0
				select n)
			{
				UniqueTargets.Pickups.Add(item);
				UniqueTargets.Spawners.Add(item);
			}
			foreach (string pickup in UniqueTargets.Pickups)
			{
				_pickupPrefabHashes.Add(StringExtensionMethods.GetStableHashCode(pickup));
			}
			CorePlugin.Log.LogInfo((object)($"UniqueItems: {_locationNames.Count} one-per-world locations hold {UniqueTargets.Pickups.Count} one-of-a-kind " + $"pickups and {UniqueTargets.Spawners.Count} creature spawners that vanilla never brings back; they return after " + $"{UniqueTargets.RespawnMinutes:0} minutes of game time (location list read in {stopwatch.ElapsedMilliseconds} ms)"));
			CorePlugin.Log.LogInfo((object)("UniqueItems: locations: " + string.Join(", ", _locationNames.OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToArray())));
			CorePlugin.Log.LogInfo((object)("UniqueItems: pickups: " + Join(UniqueTargets.Pickups)));
			CorePlugin.Log.LogInfo((object)("UniqueItems: ordinary loot left alone: " + Join(_ordinaryPickups)));
			CorePlugin.Log.LogInfo((object)((UniqueTargets.CoverSpawners.Value ? "UniqueItems: spawners: " : "UniqueItems: spawners, left as the game has them because CoverSpawners is off: ") + Join(UniqueTargets.Spawners)));
		}

		private void Collect(GameObject locationAsset, string locationName)
		{
			Pickable[] componentsInChildren = locationAsset.GetComponentsInChildren<Pickable>(true);
			foreach (Pickable val in componentsInChildren)
			{
				if (val.m_respawnTimeMinutes > 0f)
				{
					continue;
				}
				string prefabName = Utils.GetPrefabName(((Component)val).gameObject);
				if (IsOneOfAKind(val, out var items))
				{
					if (UniqueTargets.Pickups.Add(prefabName))
					{
						CorePlugin.Log.LogInfo((object)("UniqueItems: " + prefabName + " in " + locationName + " gives " + items + " and never comes back in vanilla"));
					}
				}
				else if (_ordinaryPickups.Add(prefabName))
				{
					CorePlugin.Log.LogInfo((object)("UniqueItems: " + prefabName + " in " + locationName + " left as the game has it; it gives " + items));
				}
			}
			CreatureSpawner[] componentsInChildren2 = locationAsset.GetComponentsInChildren<CreatureSpawner>(true);
			foreach (CreatureSpawner obj in componentsInChildren2)
			{
				string prefabName2 = Utils.GetPrefabName(((Component)obj).gameObject);
				if (obj.m_respawnTimeMinuts <= 0f && UniqueTargets.Spawners.Add(prefabName2))
				{
					CorePlugin.Log.LogInfo((object)("UniqueItems: " + prefabName2 + " in " + locationName + " never spawns twice in vanilla"));
				}
			}
		}

		private static bool IsOneOfAKind(Pickable pickup, out string items)
		{
			List<string> list = new List<string>();
			bool flag = false;
			foreach (GameObject item in ItemsGiven(pickup))
			{
				ItemDrop component = item.GetComponent<ItemDrop>();
				SharedData val = (((Object)(object)component == (Object)null) ? null : component.m_itemData.m_shared);
				if (val != null)
				{
					list.Add($"{Utils.GetPrefabName(item)} (quest item: {val.m_questItem}, stacks to {val.m_maxStackSize})");
					flag |= val.m_questItem || val.m_maxStackSize <= 1;
				}
			}
			items = ((list.Count > 0) ? string.Join(" + ", list.ToArray()) : "no item this mod can read");
			return flag;
		}

		private static IEnumerable<GameObject> ItemsGiven(Pickable pickup)
		{
			if ((Object)(object)pickup.m_itemPrefab != (Object)null)
			{
				yield return pickup.m_itemPrefab;
			}
			if (pickup.m_extraDrops == null || pickup.m_extraDrops.m_drops == null)
			{
				yield break;
			}
			foreach (DropData drop in pickup.m_extraDrops.m_drops)
			{
				if ((Object)(object)drop.m_item != (Object)null)
				{
					yield return drop.m_item;
				}
			}
		}

		public void StartClockIfPickedLongAgo(ZDO zdo, int prefabHash)
		{
			if (!_firstPassDone && _pickupPrefabHashes.Contains(prefabHash) && zdo.GetBool(ZDOVars.s_picked, false) && zdo.GetLong(ZDOVars.s_pickedTime, 0L) <= 1 && (!zdo.HasOwner() || zdo.IsOwner()) && Compat.TakeOwnership(zdo))
			{
				zdo.Set(ZDOVars.s_pickedTime, ZNet.instance.GetTime().Ticks);
				zdo.SetOwner(0L);
				_clocksStarted++;
			}
		}

		public void EndPass()
		{
			if (!_firstPassDone)
			{
				_firstPassDone = true;
				if (_clocksStarted > 0)
				{
					CorePlugin.Log.LogInfo((object)$"UniqueItems: started the respawn clock on {_clocksStarted} pickup(s) taken before this mod was installed");
				}
			}
		}

		private static string Join(IEnumerable<string> names)
		{
			return string.Join(", ", names.OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToArray());
		}
	}
	internal static class UniqueTargets
	{
		internal static Setting<bool> Enabled;

		internal static Setting<float> Minutes;

		internal static Setting<bool> CoverSpawners;

		internal static readonly HashSet<string> Pickups = new HashSet<string>();

		internal static readonly HashSet<string> Spawners = new HashSet<string>();

		internal static float RespawnMinutes => Math.Max(1f, Minutes.Value);

		internal static bool IsUniquePickup(string objectName)
		{
			if (Enabled.Value)
			{
				return Pickups.Contains(Utils.GetPrefabName(objectName));
			}
			return false;
		}

		internal static bool IsUniqueSpawner(string objectName)
		{
			if (Enabled.Value && CoverSpawners.Value)
			{
				return Spawners.Contains(Utils.GetPrefabName(objectName));
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Pickable), "Awake")]
	internal static class PickableAwakePatch
	{
		private static void Prefix(Pickable __instance)
		{
			if (__instance.m_respawnTimeMinutes <= 0f && UniqueTargets.IsUniquePickup(((Object)__instance).name))
			{
				__instance.m_respawnTimeMinutes = UniqueTargets.RespawnMinutes;
			}
		}
	}
	[HarmonyPatch(typeof(CreatureSpawner), "Awake")]
	internal static class CreatureSpawnerAwakePatch
	{
		private static void Prefix(CreatureSpawner __instance)
		{
			if (__instance.m_respawnTimeMinuts <= 0f && UniqueTargets.IsUniqueSpawner(((Object)__instance).name))
			{
				__instance.m_respawnTimeMinuts = UniqueTargets.RespawnMinutes;
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "Start")]
	internal static class ZoneSystemStartPatch
	{
		private static void Postfix()
		{
			UniqueItemsPlugin.Feature.Apply();
		}
	}
	[BepInPlugin("com.mysix.uniqueitems", "MySix Unique Items", "1.0.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class UniqueItemsPlugin : BaseUnityPlugin, IWorldScanConsumer
	{
		public const string PluginGuid = "com.mysix.uniqueitems";

		public const string PluginName = "MySix Unique Items";

		public const string PluginVersion = "1.0.3";

		private const int RequiredCoreApi = 1;

		private Harmony _harmony;

		public static UniqueItems Feature { get; private set; }

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if (!CorePlugin.RequireApi("MySix Unique Items", 1))
			{
				return;
			}
			Feature = new UniqueItems(((BaseUnityPlugin)this).Config);
			CorePlugin.Ready += delegate
			{
				if (CorePlugin.IsServerSide && Feature.StartClockForOldPicks)
				{
					CorePlugin.Instance.Scanner.Add((IWorldScanConsumer)(object)this);
				}
			};
			_harmony = new Harmony("com.mysix.uniqueitems");
			_harmony.PatchAll(typeof(UniqueItemsPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"MySix Unique Items 1.0.3 loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		public void BeginPass()
		{
		}

		public void Visit(ZDO zdo, int prefabHash)
		{
			Feature.StartClockIfPickedLongAgo(zdo, prefabHash);
		}

		public void EndPass(float realtimeNow)
		{
			Feature.EndPass();
		}
	}
}