Decompiled source of FrostVale ModPack 3 v3.1.2

plugins/FrostValeCompat/FrostValeCompat.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.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 FrostValeCompat
{
	[BepInPlugin("frostvale.compat", "FrostVale Compat", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class RiceWaterLevelPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "frostvale.compat";

		public const string PluginName = "FrostVale Compat";

		public const string PluginVersion = "1.1.0";

		internal static ConfigEntry<float> MinGroundOffset;

		internal static ConfigEntry<float> MaxGroundOffset;

		internal static ConfigEntry<bool> BlockRiceAwayFromWater;

		internal static ConfigEntry<bool> EnableCreatureGemDrops;

		internal static ConfigEntry<float> CreatureGemDefaultChance;

		internal static RiceWaterLevelPlugin Instance;

		internal static ManualLogSource Log;

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BlockRiceAwayFromWater = ((BaseUnityPlugin)this).Config.Bind<bool>("RtDOcean Rice", "Block rice away from water level", true, "If enabled, RtDOcean rice saplings can only be placed near the configured water-level band.");
			MinGroundOffset = ((BaseUnityPlugin)this).Config.Bind<float>("RtDOcean Rice", "Minimum ground offset from water", -0.35f, "Lowest allowed terrain height relative to water level, in meters.");
			MaxGroundOffset = ((BaseUnityPlugin)this).Config.Bind<float>("RtDOcean Rice", "Maximum ground offset from water", 0.15f, "Highest allowed terrain height relative to water level, in meters.");
			EnableCreatureGemDrops = ((BaseUnityPlugin)this).Config.Bind<bool>("Jewelcrafting Creature Drops", "Enable star-scaled creature gem drops", true, "If enabled, FrostVale adds Jewelcrafting gems to non-boss creature drops based on CLLC star level.");
			CreatureGemDefaultChance = ((BaseUnityPlugin)this).Config.Bind<float>("Jewelcrafting Creature Drops", "Default low-star unrefined drop chance", 1.5f, "Percent chance used for 0-3 star unrefined gem drops.");
			Harmony.CreateAndPatchAll(typeof(RiceWaterLevelPlugin).Assembly, "frostvale.compat");
			DiscordNameDisplayPatches.Patch();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"FrostVale compatibility patches loaded.");
		}
	}
	[HarmonyPatch(typeof(Player), "TryPlacePiece")]
	internal static class RicePlacementPatch
	{
		private static bool Prefix(Piece piece, ref bool __result)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (!RiceWaterLevelPlugin.BlockRiceAwayFromWater.Value || !RiceWaterLevelRules.IsRiceSapling(piece))
			{
				return true;
			}
			Vector3 position = ((Component)piece).transform.position;
			if (RiceWaterLevelRules.IsWithinWaterBand(position, out var groundHeight, out var waterLevel))
			{
				return true;
			}
			__result = false;
			ShowMessage($"Rice must be planted at water level. Ground {groundHeight - waterLevel:0.00}m from water.");
			return false;
		}

		private static void ShowMessage(string message)
		{
			try
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
				}
			}
			catch (Exception ex)
			{
				RiceWaterLevelPlugin.Log.LogDebug((object)("Could not show rice placement message: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Plant), "GetStatus")]
	internal static class RicePlantStatusPatch
	{
		private static void Postfix(Plant __instance, ref Status __result)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (RiceWaterLevelPlugin.BlockRiceAwayFromWater.Value && (int)__result == 0 && RiceWaterLevelRules.IsRicePlant(__instance) && !RiceWaterLevelRules.IsWithinWaterBand(((Component)__instance).transform.position, out var _, out var _))
			{
				__result = (Status)3;
			}
		}
	}
	internal static class RiceWaterLevelRules
	{
		public static bool IsRiceSapling(Piece piece)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			return LooksLikeRice(((Object)piece).name) || LooksLikeRice(((Object)((Component)piece).gameObject).name) || LooksLikeRice(piece.m_name);
		}

		public static bool IsRicePlant(Plant plant)
		{
			if ((Object)(object)plant == (Object)null)
			{
				return false;
			}
			return LooksLikeRice(((Object)plant).name) || LooksLikeRice(((Object)((Component)plant).gameObject).name) || LooksLikeRice(plant.m_name);
		}

		public static bool IsWithinWaterBand(Vector3 position, out float groundHeight, out float waterLevel)
		{
			//IL_0020: 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)
			ZoneSystem instance = ZoneSystem.instance;
			waterLevel = (((Object)(object)instance != (Object)null) ? instance.m_waterLevel : 30f);
			groundHeight = position.y;
			float num = default(float);
			if ((Object)(object)instance != (Object)null && instance.GetGroundHeight(position, ref num))
			{
				groundHeight = num;
			}
			float num2 = groundHeight - waterLevel;
			return num2 >= RiceWaterLevelPlugin.MinGroundOffset.Value && num2 <= RiceWaterLevelPlugin.MaxGroundOffset.Value;
		}

		private static bool LooksLikeRice(string value)
		{
			return !string.IsNullOrEmpty(value) && value.IndexOf("Rice", StringComparison.OrdinalIgnoreCase) >= 0 && value.IndexOf("_RtD", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
	internal static class DiscordNameDisplayPatches
	{
		private static readonly Dictionary<string, string> NamesById = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static readonly Regex NumericIdPattern = new Regex("\\b\\d{15,20}\\b", RegexOptions.Compiled);

		public static void Patch()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			try
			{
				Harmony harmony = new Harmony("frostvale.compat.discord-names");
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnPlayerJoin", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnPlayerLeave", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnClientLoginArtifacts", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "SendClientLogOnRequest", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.ClientLogRelay.ClientLogRelayIntegration", "PostDisconnectLog", typeof(DiscordPlayerNameTracker).GetMethod("IdFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.ClientLogRelay.ClientLogRelayIntegration", "PostDisconnectLogAsync", typeof(DiscordPlayerNameTracker).GetMethod("IdFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordStatusTracker", "RecordJoin", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordStatusTracker", "RecordLeave", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendMessage", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendEmbed", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendEmbedWithFiles", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
				PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "EnqueuePost", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
				RiceWaterLevelPlugin.Log.LogInfo((object)"FrostVale Discord name-display patch loaded.");
			}
			catch (Exception ex)
			{
				RiceWaterLevelPlugin.Log.LogWarning((object)("FrostVale Discord name-display patch skipped: " + ex.Message));
			}
		}

		private static void PatchPrefix(Harmony harmony, string typeName, string methodName, MethodInfo prefix)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			if (prefix == null)
			{
				return;
			}
			Type type = AccessTools.TypeByName(typeName);
			if (type == null)
			{
				return;
			}
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
			{
				if (declaredMethod.Name == methodName)
				{
					harmony.Patch((MethodBase)declaredMethod, new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		internal static void Remember(string platformId, string playerName)
		{
			if (!string.IsNullOrWhiteSpace(platformId) && !string.IsNullOrWhiteSpace(playerName) && !IsRawId(playerName))
			{
				NamesById[platformId.Trim()] = playerName.Trim();
			}
		}

		internal static string RewriteText(string value)
		{
			if (string.IsNullOrEmpty(value) || NamesById.Count == 0)
			{
				return value;
			}
			string value2;
			return NumericIdPattern.Replace(value, (Match match) => NamesById.TryGetValue(match.Value, out value2) ? value2 : match.Value);
		}

		internal static bool IsRawId(string value)
		{
			return !string.IsNullOrWhiteSpace(value) && NumericIdPattern.IsMatch(value.Trim());
		}
	}
	internal static class DiscordPlayerNameTracker
	{
		private static void PlayerNameFirstPrefix(object[] __args)
		{
			if (__args != null && __args.Length >= 2)
			{
				DiscordNameDisplayPatches.Remember(__args[1] as string, __args[0] as string);
			}
		}

		private static void IdFirstPrefix(object[] __args)
		{
			if (__args != null && __args.Length >= 2)
			{
				DiscordNameDisplayPatches.Remember(__args[0] as string, __args[1] as string);
			}
		}
	}
	internal static class DiscordVisibleTextRewriter
	{
		private static void Prefix(object[] __args)
		{
			if (__args == null)
			{
				return;
			}
			for (int i = 0; i < __args.Length; i++)
			{
				object obj = __args[i];
				if (obj is string value)
				{
					__args[i] = DiscordNameDisplayPatches.RewriteText(value);
				}
				else
				{
					RewriteObject(obj, new HashSet<object>(ReferenceEqualityComparer.Instance));
				}
			}
		}

		private static void RewriteObject(object value, HashSet<object> visited)
		{
			if (value == null || value is string || value.GetType().IsPrimitive || !visited.Add(value))
			{
				return;
			}
			if (value is IDictionary dictionary)
			{
				List<object> list = new List<object>();
				foreach (object key in dictionary.Keys)
				{
					list.Add(key);
				}
				{
					foreach (object item in list)
					{
						object obj = dictionary[item];
						string text = obj as string;
						dictionary[item] = ((text != null) ? DiscordNameDisplayPatches.RewriteText(text) : RewriteAndReturn(obj, visited));
					}
					return;
				}
			}
			if (value is IList list2)
			{
				for (int i = 0; i < list2.Count; i++)
				{
					object obj2 = list2[i];
					string text2 = obj2 as string;
					list2[i] = ((text2 != null) ? DiscordNameDisplayPatches.RewriteText(text2) : RewriteAndReturn(obj2, visited));
				}
				return;
			}
			FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(string))
				{
					string value2 = fieldInfo.GetValue(value) as string;
					fieldInfo.SetValue(value, DiscordNameDisplayPatches.RewriteText(value2));
				}
				else if (!fieldInfo.FieldType.IsValueType)
				{
					RewriteObject(fieldInfo.GetValue(value), visited);
				}
			}
		}

		private static object RewriteAndReturn(object value, HashSet<object> visited)
		{
			RewriteObject(value, visited);
			return value;
		}
	}
	[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
	internal static class JewelcraftingCreatureDropPatch
	{
		private static readonly FieldRef<CharacterDrop, Character> CharacterRef = AccessTools.FieldRefAccess<CharacterDrop, Character>("m_character");

		private static readonly string[] UnrefinedGemPrefabs = new string[7] { "Uncut_Green_Stone", "Uncut_Orange_Stone", "Uncut_Black_Stone", "Uncut_Red_Stone", "Uncut_Blue_Stone", "Uncut_Purple_Stone", "Uncut_Yellow_Stone" };

		private static readonly string[] SimpleGemPrefabs = new string[7] { "Simple_Green_Socket", "Simple_Orange_Socket", "Simple_Black_Socket", "Simple_Red_Socket", "Simple_Blue_Socket", "Simple_Purple_Socket", "Simple_Yellow_Socket" };

		private static readonly string[] AdvancedGemPrefabs = new string[7] { "Advanced_Green_Socket", "Advanced_Orange_Socket", "Advanced_Black_Socket", "Advanced_Red_Socket", "Advanced_Blue_Socket", "Advanced_Purple_Socket", "Advanced_Yellow_Socket" };

		private static readonly HashSet<string> MissingPrefabWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static void Postfix(CharacterDrop __instance, ref List<KeyValuePair<GameObject, int>> __result)
		{
			if (!RiceWaterLevelPlugin.EnableCreatureGemDrops.Value || (Object)(object)__instance == (Object)null || __result == null)
			{
				return;
			}
			Character val = CharacterRef.Invoke(__instance);
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)__instance).GetComponent<Character>();
			}
			if (!((Object)(object)val == (Object)null) && !val.IsBoss())
			{
				switch (Math.Max(0, Math.Min(5, val.GetLevel() - 1)))
				{
				case 0:
					AddDefaultGatedDrops(__result, UnrefinedGemPrefabs, 1);
					break;
				case 1:
					AddDefaultGatedDrops(__result, UnrefinedGemPrefabs, 2);
					break;
				case 2:
					AddDefaultGatedDrops(__result, UnrefinedGemPrefabs, 3);
					break;
				case 3:
					AddRandomDrops(__result, SimpleGemPrefabs, 0, 1);
					AddDefaultGatedDrops(__result, UnrefinedGemPrefabs, 2);
					break;
				case 4:
					AddRandomDrops(__result, SimpleGemPrefabs, 0, 2);
					AddRandomDrops(__result, UnrefinedGemPrefabs, 1, 3);
					break;
				default:
					AddRandomDrops(__result, AdvancedGemPrefabs, 0, 2);
					AddRandomDrops(__result, UnrefinedGemPrefabs, 2, 4);
					break;
				}
			}
		}

		private static void AddDefaultGatedDrops(List<KeyValuePair<GameObject, int>> drops, string[] prefabNames, int maxAmount)
		{
			float num = Mathf.Clamp(RiceWaterLevelPlugin.CreatureGemDefaultChance.Value, 0f, 100f);
			if (!(Random.Range(0f, 100f) >= num))
			{
				AddRandomDrops(drops, prefabNames, 1, maxAmount);
			}
		}

		private static void AddRandomDrops(List<KeyValuePair<GameObject, int>> drops, string[] prefabNames, int minAmount, int maxAmount)
		{
			int num = Random.Range(minAmount, maxAmount + 1);
			for (int i = 0; i < num; i++)
			{
				GameObject randomPrefab = GetRandomPrefab(prefabNames);
				if ((Object)(object)randomPrefab != (Object)null)
				{
					drops.Add(new KeyValuePair<GameObject, int>(randomPrefab, 1));
				}
			}
		}

		private static GameObject GetRandomPrefab(string[] prefabNames)
		{
			if (prefabNames == null || prefabNames.Length == 0)
			{
				return null;
			}
			string text = prefabNames[Random.Range(0, prefabNames.Length)];
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(text) : null);
			if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				val = ZNetScene.instance.GetPrefab(text);
			}
			if ((Object)(object)val == (Object)null && MissingPrefabWarnings.Add(text))
			{
				RiceWaterLevelPlugin.Log.LogWarning((object)("FrostVale Jewelcrafting drop prefab not found: " + text));
			}
			return val;
		}
	}
	internal sealed class ReferenceEqualityComparer : IEqualityComparer<object>
	{
		public static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer();

		private ReferenceEqualityComparer()
		{
		}

		public new bool Equals(object x, object y)
		{
			return x == y;
		}

		public int GetHashCode(object obj)
		{
			return RuntimeHelpers.GetHashCode(obj);
		}
	}
}