Decompiled source of PapysLand VSG QoL v0.3.6

PapysLandVSGQoL.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PapysLandVSGQoL")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PapysLandVSGQoL")]
[assembly: AssemblyTitle("PapysLandVSGQoL")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PapysLandVSGQoL
{
	[BepInPlugin("papysland.vsgqol", "PapysLand VSG QoL", "0.3.6")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static Plugin Instance;

		private Harmony _harmony;

		private int _reconcileFrames = -1;

		private bool _reconciling;

		private float _nextPeriodicReconcile;

		private static Type _playerType;

		private static Type _dispatcherType;

		private static Type _chainStateType;

		private static Type _itemAcquiredType;

		private static FieldInfo _indexedConfigField;

		private static MethodInfo _getStep;

		private static MethodInfo _getCounter;

		private static MethodInfo _setCounter;

		private static MethodInfo _isComplete;

		private static MethodInfo _advanceChain;

		private static MethodInfo _countInInventory;

		private static MethodInfo _getEffectiveGoals;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("papysland.vsgqol");
			Log.LogInfo((object)"PapysLand VSG QoL 0.3.6 LOOP GUARD chargé.");
			Log.LogInfo((object)"Mode: compteur = contenu REEL de l'inventaire; contrôle périodique 0,20 s; aucun faux item_acquired.");
			try
			{
				ResolveVsg();
				PatchInventoryChanged();
				PatchInventoryRemoveMethods();
				PatchHumanoidDropItem();
				PatchAdvanceChain();
				ScheduleReconcile(3);
				Log.LogInfo((object)"PapysLand VSG QoL 0.3.6 prêt.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Initialisation 0.3.0 impossible: " + ex));
			}
		}

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

		private void Update()
		{
			if (Time.unscaledTime >= _nextPeriodicReconcile)
			{
				_nextPeriodicReconcile = Time.unscaledTime + 0.2f;
				ReconcileAllActiveInventoryCounters();
			}
			if (_reconcileFrames >= 0)
			{
				if (_reconcileFrames > 0)
				{
					_reconcileFrames--;
					return;
				}
				_reconcileFrames = -1;
				ReconcileAllActiveInventoryCounters();
			}
		}

		internal static void ScheduleReconcile(int frames = 2)
		{
			if (!((Object)(object)Instance == (Object)null) && Instance._reconcileFrames < frames)
			{
				Instance._reconcileFrames = frames;
			}
		}

		private static void ResolveVsg()
		{
			_playerType = AccessTools.TypeByName("Player") ?? throw new Exception("Type Player introuvable.");
			_dispatcherType = AccessTools.TypeByName("ValheimServerGuide.Triggers.GuidanceDispatcher") ?? throw new Exception("GuidanceDispatcher introuvable.");
			_chainStateType = AccessTools.TypeByName("ValheimServerGuide.State.ChainState") ?? throw new Exception("ChainState introuvable.");
			_itemAcquiredType = AccessTools.TypeByName("ValheimServerGuide.Triggers.ItemAcquiredTrigger") ?? throw new Exception("ItemAcquiredTrigger introuvable.");
			_indexedConfigField = AccessTools.Field(_dispatcherType, "_indexedConfig") ?? throw new Exception("GuidanceDispatcher._indexedConfig introuvable.");
			_getStep = AccessTools.Method(_chainStateType, "GetStep", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.GetStep introuvable.");
			_getCounter = AccessTools.Method(_chainStateType, "GetCounter", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.GetCounter introuvable.");
			_setCounter = AccessTools.Method(_chainStateType, "SetCounter", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.SetCounter introuvable.");
			_isComplete = AccessTools.Method(_chainStateType, "IsComplete", (Type[])null, (Type[])null) ?? throw new Exception("ChainState.IsComplete introuvable.");
			_advanceChain = AccessTools.Method(_dispatcherType, "AdvanceChain", (Type[])null, (Type[])null) ?? throw new Exception("GuidanceDispatcher.AdvanceChain introuvable.");
			_countInInventory = AccessTools.Method(_itemAcquiredType, "CountInInventory", (Type[])null, (Type[])null) ?? throw new Exception("ItemAcquiredTrigger.CountInInventory introuvable.");
			_getEffectiveGoals = AccessTools.Method(_itemAcquiredType, "GetEffectiveGoals", (Type[])null, (Type[])null) ?? throw new Exception("ItemAcquiredTrigger.GetEffectiveGoals introuvable.");
			Log.LogInfo((object)"VSG 0.16.1: méthodes LIVE INVENTORY + CHAIN GOALS résolues.");
		}

		private void PatchInventoryChanged()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Inventory");
			MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "Changed", (Type[])null, (Type[])null));
			if (methodInfo == null)
			{
				throw new Exception("Inventory.Changed introuvable.");
			}
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "InventoryChangedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"Patch Inventory.Changed actif.");
		}

		private void PatchInventoryRemoveMethods()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Inventory");
			if (type == null)
			{
				throw new Exception("Type Inventory introuvable.");
			}
			int num = 0;
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (!(declaredMethod == null) && (string.Equals(declaredMethod.Name, "RemoveItem", StringComparison.Ordinal) || string.Equals(declaredMethod.Name, "RemoveOneItem", StringComparison.Ordinal)))
				{
					_harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "InventoryMutationPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			Log.LogInfo((object)$"Patch Inventory.RemoveItem/RemoveOneItem actif ({num} surcharge(s)).");
		}

		private void PatchHumanoidDropItem()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Humanoid");
			if (type == null)
			{
				throw new Exception("Type Humanoid introuvable.");
			}
			int num = 0;
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (!(declaredMethod == null) && string.Equals(declaredMethod.Name, "DropItem", StringComparison.Ordinal))
				{
					_harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "DropItemPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			Log.LogInfo((object)$"Patch Humanoid.DropItem actif ({num} surcharge(s)).");
		}

		public static void InventoryMutationPostfix(object __instance)
		{
			try
			{
				object localInventory = GetLocalInventory();
				if (localInventory != null && localInventory == __instance)
				{
					ScheduleReconcile();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("InventoryMutationPostfix: " + ex.Message));
				}
			}
		}

		public static void DropItemPostfix(object __instance)
		{
			try
			{
				object localPlayer = GetLocalPlayer();
				if (localPlayer != null && localPlayer == __instance)
				{
					ScheduleReconcile(3);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("DropItemPostfix: " + ex.Message));
				}
			}
		}

		private void PatchAdvanceChain()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			_harmony.Patch((MethodBase)_advanceChain, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AdvanceChainPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"Patch GuidanceDispatcher.AdvanceChain actif.");
		}

		public static void InventoryChangedPostfix(object __instance)
		{
			try
			{
				object localInventory = GetLocalInventory();
				if (localInventory != null && localInventory == __instance)
				{
					ScheduleReconcile();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("InventoryChangedPostfix: " + ex.Message));
				}
			}
		}

		public static void AdvanceChainPostfix()
		{
			ScheduleReconcile(3);
		}

		private void ReconcileAllActiveInventoryCounters()
		{
			if (_reconciling)
			{
				return;
			}
			object localPlayer = GetLocalPlayer();
			if (localPlayer == null)
			{
				return;
			}
			object value = _indexedConfigField.GetValue(null);
			if (value == null || !(GetMember(value, "Guidances") is IEnumerable enumerable))
			{
				return;
			}
			_reconciling = true;
			try
			{
				for (int i = 0; i < 12; i++)
				{
					bool flag = false;
					foreach (object item in enumerable)
					{
						if (item == null)
						{
							continue;
						}
						string text = GetString(item, "Id");
						if (string.IsNullOrWhiteSpace(text) || Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { localPlayer, text })) || !(GetMember(item, "Steps") is IList { Count: not 0 } list))
						{
							continue;
						}
						int num = Convert.ToInt32(_getStep.Invoke(null, new object[2] { localPlayer, text }));
						if (num < 0 || num >= list.Count)
						{
							continue;
						}
						object obj = list[num];
						if (obj == null)
						{
							continue;
						}
						object member = GetMember(obj, "Trigger");
						if (member != null && Eq(GetString(member, "Type"), "entry_finished"))
						{
							string text2 = GetString(member, "Entry");
							if (!string.IsNullOrWhiteSpace(text2) && Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { localPlayer, text2 })))
							{
								List<string> list2 = new List<string>();
								_advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list2 });
								Log.LogInfo((object)("[PREREQ] " + text + ": prérequis " + text2 + " déjà terminé -> étape avancée."));
								string[] array = list2.ToArray();
								for (int j = 0; j < array.Length; j++)
								{
									PropagateEntryFinished(array[j], localPlayer, enumerable);
								}
								flag = true;
								break;
							}
						}
						int num2 = GetInt(obj, "ProgressGoal", 0);
						object member2 = GetMember(obj, "ProgressTrigger");
						if (num2 > 0 && member2 != null && Eq(GetString(member2, "Type"), "item_acquired"))
						{
							string text3 = GetString(member2, "Item");
							if (string.IsNullOrWhiteSpace(text3))
							{
								text3 = GetString(member2, "Prefab");
							}
							if (!string.IsNullOrWhiteSpace(text3))
							{
								int num3 = Convert.ToInt32(_countInInventory.Invoke(null, new object[2] { localPlayer, text3 }));
								int num4 = Convert.ToInt32(_getCounter.Invoke(null, new object[3] { localPlayer, text, num }));
								if (num4 != num3)
								{
									_setCounter.Invoke(null, new object[4] { localPlayer, text, num, num3 });
									Log.LogInfo((object)$"[LIVE] {text} étape {num + 1}: {text3} = {num3}/{num2} (ancien {num4})");
								}
								if (num3 >= num2)
								{
									List<string> list3 = new List<string>();
									_advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list3 });
									Log.LogInfo((object)$"[LIVE] Objectif compteur satisfait: {text} {text3} {num3}/{num2} -> étape avancée.");
									string[] array = list3.ToArray();
									for (int j = 0; j < array.Length; j++)
									{
										PropagateEntryFinished(array[j], localPlayer, enumerable);
									}
									flag = true;
									break;
								}
							}
						}
						object member3 = GetMember(obj, "Trigger");
						if (member3 == null || !Eq(GetString(member3, "Type"), "item_acquired"))
						{
							continue;
						}
						object obj2 = null;
						try
						{
							obj2 = _getEffectiveGoals.Invoke(null, new object[1] { member3 });
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("GetEffectiveGoals: " + ex.Message));
						}
						if (!(obj2 is IEnumerable enumerable2))
						{
							continue;
						}
						bool flag2 = false;
						bool flag3 = true;
						List<string> list4 = new List<string>();
						foreach (object item2 in enumerable2)
						{
							if (item2 == null)
							{
								continue;
							}
							flag2 = true;
							string text4 = GetString(item2, "Item");
							if (string.IsNullOrWhiteSpace(text4))
							{
								text4 = GetString(item2, "Prefab");
							}
							int num5 = GetInt(item2, "Count", 1);
							if (num5 <= 0)
							{
								num5 = 1;
							}
							if (string.IsNullOrWhiteSpace(text4))
							{
								flag3 = false;
								continue;
							}
							int num6 = Convert.ToInt32(_countInInventory.Invoke(null, new object[2] { localPlayer, text4 }));
							list4.Add($"{text4} {num6}/{num5}");
							if (num6 < num5)
							{
								flag3 = false;
							}
						}
						if (flag2 && flag3)
						{
							List<string> list5 = new List<string>();
							_advanceChain.Invoke(null, new object[4] { item, num, localPlayer, list5 });
							Log.LogInfo((object)string.Format("[LIVE-GOALS] {0} étape {1}: {2} -> étape avancée.", text, num + 1, string.Join(", ", list4)));
							string[] array = list5.ToArray();
							for (int j = 0; j < array.Length; j++)
							{
								PropagateEntryFinished(array[j], localPlayer, enumerable);
							}
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						break;
					}
				}
			}
			catch (TargetInvocationException ex2)
			{
				Log.LogError((object)("Réconciliation VSG: " + (ex2.InnerException ?? ex2)));
			}
			catch (Exception ex3)
			{
				Log.LogError((object)("Réconciliation VSG: " + ex3));
			}
			finally
			{
				_reconciling = false;
			}
		}

		private static void PropagateEntryFinished(string completedId, object player, IEnumerable guidances)
		{
			if (string.IsNullOrWhiteSpace(completedId) || player == null || guidances == null)
			{
				return;
			}
			try
			{
				foreach (object guidance in guidances)
				{
					if (guidance == null)
					{
						continue;
					}
					string text = GetString(guidance, "Id");
					if (string.IsNullOrWhiteSpace(text) || Convert.ToBoolean(_isComplete.Invoke(null, new object[2] { player, text })) || !(GetMember(guidance, "Steps") is IList { Count: not 0 } list))
					{
						continue;
					}
					int num = Convert.ToInt32(_getStep.Invoke(null, new object[2] { player, text }));
					if (num < 0 || num >= list.Count)
					{
						continue;
					}
					object member = GetMember(list[num], "Trigger");
					if (member != null && Eq(GetString(member, "Type"), "entry_finished") && string.Equals(GetString(member, "Entry")?.Trim(), completedId.Trim(), StringComparison.OrdinalIgnoreCase))
					{
						List<string> list2 = new List<string>();
						_advanceChain.Invoke(null, new object[4] { guidance, num, player, list2 });
						Log.LogInfo((object)("[CHAIN] " + completedId + " terminé -> " + text + " déverrouillée."));
						string[] array = list2.ToArray();
						for (int i = 0; i < array.Length; i++)
						{
							PropagateEntryFinished(array[i], player, guidances);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Propagation entry_finished: " + ex));
			}
		}

		private static object GetLocalPlayer()
		{
			if (_playerType == null)
			{
				return null;
			}
			return AccessTools.Field(_playerType, "m_localPlayer")?.GetValue(null);
		}

		private static object GetLocalInventory()
		{
			object localPlayer = GetLocalPlayer();
			if (localPlayer == null)
			{
				return null;
			}
			return AccessTools.Method(localPlayer.GetType(), "GetInventory", (Type[])null, (Type[])null)?.Invoke(localPlayer, null);
		}

		private static object GetMember(object obj, string name)
		{
			if (obj == null)
			{
				return null;
			}
			Type type = obj.GetType();
			PropertyInfo propertyInfo = AccessTools.Property(type, name);
			if (propertyInfo != null)
			{
				try
				{
					return propertyInfo.GetValue(obj, null);
				}
				catch
				{
				}
			}
			FieldInfo fieldInfo = AccessTools.Field(type, name);
			if (fieldInfo != null)
			{
				try
				{
					return fieldInfo.GetValue(obj);
				}
				catch
				{
				}
			}
			return null;
		}

		private static string GetString(object obj, string name)
		{
			return GetMember(obj, name)?.ToString();
		}

		private static int GetInt(object obj, string name, int fallback)
		{
			object member = GetMember(obj, name);
			if (member == null)
			{
				return fallback;
			}
			try
			{
				return Convert.ToInt32(member);
			}
			catch
			{
				return fallback;
			}
		}

		private static bool Eq(string a, string b)
		{
			return string.Equals(a?.Trim(), b, StringComparison.OrdinalIgnoreCase);
		}
	}
}