Decompiled source of BukeperryMod v0.3.0

BukeperryMod.dll

Decompiled a week ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BukeperryMod")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+88f32dad47cc8d087e01109e76206ee635942799")]
[assembly: AssemblyProduct("BukeperryMod")]
[assembly: AssemblyTitle("BukeperryMod")]
[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 BukeperryMod
{
	[HarmonyPatch]
	public static class BukeperryAIPatches
	{
		[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
		{
			typeof(Character),
			typeof(Character)
		})]
		[HarmonyPrefix]
		public static bool IsEnemyPrefix(Character a, Character b, ref bool __result)
		{
			BukeperryController bukeperryController = (Object.op_Implicit((Object)(object)a) ? ((Component)a).GetComponent<BukeperryController>() : null);
			if ((Object)(object)bukeperryController != (Object)null)
			{
				Player val = (Player)(object)((b is Player) ? b : null);
				if (val != null)
				{
					__result = bukeperryController.IsHostileTo(val);
					return false;
				}
			}
			BukeperryController bukeperryController2 = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BukeperryController>() : null);
			if ((Object)(object)bukeperryController2 != (Object)null && a is Player)
			{
				__result = true;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		[HarmonyPrefix]
		public static void RPC_DamagePrefix(Character __instance, long sender, HitData hit)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			BukeperryController component = ((Component)__instance).GetComponent<BukeperryController>();
			if (!((Object)(object)component == (Object)null))
			{
				Character attacker = hit.GetAttacker();
				Player val = (Player)(object)((attacker is Player) ? attacker : null);
				if ((Object)(object)val == (Object)null && hit.HaveAttacker())
				{
					val = FindPlayerByZDOID(hit.m_attacker);
				}
				if ((Object)(object)val != (Object)null)
				{
					component.OnAttackedBy(val);
				}
			}
		}

		public static Player FindPlayerByZDOID(ZDOID id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (id == ZDOID.None)
			{
				return null;
			}
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				GameObject val = ZNetScene.instance.FindInstance(id);
				Player result = default(Player);
				if ((Object)(object)val != (Object)null && val.TryGetComponent<Player>(ref result))
				{
					return result;
				}
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (((Character)allPlayer).GetZDOID() == id)
				{
					return allPlayer;
				}
			}
			return null;
		}

		[HarmonyPatch(typeof(ZoneSystem), "Start")]
		[HarmonyPostfix]
		public static void OnZoneSystemStart(ZoneSystem __instance)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				BukeperrySpawner.LogMarkedLocation();
			}
		}

		[HarmonyPatch(typeof(Trader), "Update")]
		[HarmonyPrefix]
		public static bool TraderUpdatePrefix(Trader __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<BukeperryController>() != (Object)null)
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Trader), "RandomTalk")]
		[HarmonyPrefix]
		public static bool TraderRandomTalkPrefix(Trader __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<BukeperryController>() != (Object)null)
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Character), "GetHoverText")]
		[HarmonyPrefix]
		public static bool TraderGetHoverTextPrefix(Character __instance, ref string __result)
		{
			Trader component = ((Component)__instance).GetComponent<Trader>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			BukeperryController component2 = ((Component)__instance).GetComponent<BukeperryController>();
			if ((Object)(object)component2 == (Object)null)
			{
				return true;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && component2.IsHostileTo(Player.m_localPlayer))
			{
				__result = "";
				return false;
			}
			__result = component.GetHoverText();
			return false;
		}

		[HarmonyPatch(typeof(Trader), "Interact")]
		[HarmonyPrefix]
		public static bool TraderInteractPrefix(Trader __instance, Humanoid character, bool hold, ref bool __result)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			BukeperryController component = ((Component)__instance).GetComponent<BukeperryController>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			if (hold)
			{
				__result = false;
				return false;
			}
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val != null && component.IsHostileTo(val))
			{
				__result = false;
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null)
			{
				Chat.instance.SetNpcText(((Component)__instance).gameObject, Vector3.up * 2.5f, 20f, 4f, "", "bukeperry have goods. trade now.", false);
			}
			StoreGui.instance.Show(__instance);
			__result = true;
			return false;
		}

		[HarmonyPatch(typeof(StoreGui), "BuySelectedItem")]
		[HarmonyPrefix]
		public static bool BuySelectedItemPrefix(StoreGui __instance)
		{
			//IL_00d6: 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)
			Trader value = Traverse.Create((object)__instance).Field<Trader>("m_trader").Value;
			if ((Object)(object)value == (Object)null || (Object)(object)((Component)value).GetComponent<BukeperryController>() == (Object)null)
			{
				return true;
			}
			TradeItem value2 = Traverse.Create((object)__instance).Field<TradeItem>("m_selectedItem").Value;
			if (value2 != null && (Object)(object)value2.m_prefab != (Object)null && ((Object)value2.m_prefab).name == "Wood")
			{
				__instance.Hide();
				if ((Object)(object)Chat.instance != (Object)null)
				{
					string[] array = new string[3] { "bukeperry need wood. no have extra. come back later.", "my wood! you no touch tree! go away tiny viking!", "10000 gold good, but wood better. troll keep wood" };
					string text = array[Random.Range(0, array.Length)];
					Chat.instance.SetNpcText(((Component)value).gameObject, Vector3.up * 2.5f, 20f, 5f, "", text, false);
				}
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Chat), "SendText")]
		[HarmonyPrefix]
		public static void SendTextPrefix(Type type, string text)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected I4, but got Unknown
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected I4, but got Unknown
			if (string.IsNullOrWhiteSpace(text) || (int)type == 3 || !((Object)(object)Player.m_localPlayer != (Object)null))
			{
				return;
			}
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			string playerName = Player.m_localPlayer.GetPlayerName();
			BukeperryPlugin.Log.LogInfo((object)$"[Chat Sent] {playerName} ({type}): \"{text}\"");
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "BukeperryPlayerChatRPC", new object[4]
					{
						position,
						(int)type,
						text,
						playerName
					});
				}
			}
			else if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				ProcessChatMessage(position, (int)type, playerName, text);
			}
		}

		public static void OnBukeperryPlayerChatRPC(long sender, Vector3 position, int type, string text, string speakerName)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			BukeperryPlugin.Log.LogInfo((object)$"[Server RPC] Received chat from {speakerName} ({position}): \"{text}\"");
			ProcessChatMessage(position, type, speakerName, text);
		}

		private static void ProcessChatMessage(Vector3 position, int type, string speakerName, string text)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if (type == 3 || string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			string text2 = text.Trim();
			if (text2.Equals("!bukeperry", StringComparison.OrdinalIgnoreCase) || text2.Equals("!spawn_bukeperry", StringComparison.OrdinalIgnoreCase))
			{
				BukeperryPlugin.Log.LogInfo((object)("Manual Bukeperry spawn check requested via chat by " + speakerName));
				BukeperrySpawner.CheckAndSpawn();
			}
			else
			{
				if (text.StartsWith("/"))
				{
					return;
				}
				float num = BukeperryPlugin.ProximityRadiusConfig?.Value ?? 20f;
				float num2 = ((type == 2) ? Mathf.Max(num, 70f) : num);
				if (!BukeperryController.TryGetBukeperryLocation(out var position2, out var bukeperryZDOID))
				{
					BukeperryPlugin.Log.LogWarning((object)("[Chat Check] Could not locate Bukeperry in world to hear " + speakerName + "."));
					return;
				}
				float num3 = Vector3.Distance(position, position2);
				if (num3 <= num2)
				{
					string text3 = ((type == 2) ? "shout" : "chat");
					BukeperryPlugin.Log.LogInfo((object)$"Bukeperry overheard {speakerName}'s {text3} ({num3:F1}m away, max {num2:F0}m): \"{text}\"");
					BukeperryChatClient.SendPrompt(text, delegate(string reply)
					{
						//IL_001d: Unknown result type (might be due to invalid IL or missing references)
						BukeperryPlugin.Log.LogInfo((object)("[Bukeperry Reply - Main Thread]: \"" + reply + "\""));
						BukeperryController.Speak(bukeperryZDOID, reply);
					});
				}
				else
				{
					BukeperryPlugin.Log.LogInfo((object)$"[Chat Check] {speakerName} spoke, but Bukeperry is too far away ({num3:F1}m > {num2:F1}m).");
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		[HarmonyPostfix]
		public static void ZNetAwakePostfix()
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<ZDOID, string>("BukeperrySpeechRPC", (Action<long, ZDOID, string>)BukeperryController.OnBukeperrySpeechRPC);
				ZRoutedRpc.instance.Register<Vector3, int, string, string>("BukeperryPlayerChatRPC", (Method<Vector3, int, string, string>)OnBukeperryPlayerChatRPC);
			}
		}
	}
	public static class BukeperryChatClient
	{
		public static readonly ConcurrentQueue<Action> MainThreadQueue = new ConcurrentQueue<Action>();

		private static readonly HttpClient s_httpClient = new HttpClient
		{
			Timeout = TimeSpan.FromSeconds(35.0)
		};

		private static bool s_isPending = false;

		public static void SendPrompt(string prompt, Action<string> onReply)
		{
			string endpoint = BukeperryPlugin.ApiEndpointConfig?.Value;
			string apiKey = BukeperryPlugin.ApiKeyConfig?.Value;
			string channelId = BukeperryPlugin.ChannelIdConfig?.Value;
			if (string.IsNullOrWhiteSpace(endpoint) || string.IsNullOrWhiteSpace(apiKey))
			{
				BukeperryPlugin.Log.LogWarning((object)"Bukeperry chat ApiEndpoint or ApiKey is not configured in config file.");
				return;
			}
			if (s_isPending)
			{
				BukeperryPlugin.Log.LogInfo((object)"Bukeperry is still thinking about the last message. Ignoring new prompt.");
				return;
			}
			s_isPending = true;
			Task.Run(async delegate
			{
				try
				{
					string escapedPrompt = EscapeJson(prompt);
					string jsonBody;
					if (!string.IsNullOrWhiteSpace(channelId))
					{
						string escapedChannel = EscapeJson(channelId);
						jsonBody = "{\"prompt\":\"" + escapedPrompt + "\",\"channelId\":\"" + escapedChannel + "\"}";
					}
					else
					{
						jsonBody = "{\"prompt\":\"" + escapedPrompt + "\"}";
					}
					using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, endpoint);
					request.Headers.Add("x-api-key", apiKey);
					request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
					HttpResponseMessage response = await s_httpClient.SendAsync(request);
					string responseBody = await response.Content.ReadAsStringAsync();
					if (!response.IsSuccessStatusCode)
					{
						BukeperryPlugin.Log.LogError((object)$"Chat API failed with HTTP {(int)response.StatusCode}: {responseBody}");
						string fallback = ExtractReply(responseBody);
						if (!string.IsNullOrWhiteSpace(fallback))
						{
							MainThreadQueue.Enqueue(delegate
							{
								onReply(fallback);
							});
						}
						return;
					}
					string reply = ExtractReply(responseBody);
					if (!string.IsNullOrWhiteSpace(reply))
					{
						MainThreadQueue.Enqueue(delegate
						{
							onReply(reply);
						});
					}
				}
				catch (OperationCanceledException)
				{
					BukeperryPlugin.Log.LogWarning((object)"Bukeperry chat API request timed out (took longer than 35s).");
				}
				catch (Exception ex2)
				{
					Exception ex3 = ex2;
					BukeperryPlugin.Log.LogError((object)("Error calling Bukeperry chat API: " + ex3.Message));
				}
				finally
				{
					s_isPending = false;
				}
			});
		}

		private static string EscapeJson(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n")
				.Replace("\r", "\\r")
				.Replace("\t", "\\t");
		}

		private static string ExtractReply(string json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return null;
			}
			Match match = Regex.Match(json, "\"reply\"\\s*:\\s*\"((?:\\\\.|[^\"\\\\])*)\"");
			if (match.Success)
			{
				return Regex.Unescape(match.Groups[1].Value);
			}
			return null;
		}
	}
	public class BukeperryController : MonoBehaviour
	{
		public static readonly List<BukeperryController> Instances = new List<BukeperryController>();

		public const string GrudgeZdoKey = "Bukeperry_Grudges";

		public const string LastAttackTimeZdoKey = "Bukeperry_LastAttackTime";

		public const float DeaggroDistance = 50f;

		public const float OutOfRangeTimeout = 10f;

		public const float DisengageTimeout = 30f;

		private ZNetView m_nview;

		private readonly HashSet<long> m_hostilePlayerIDs = new HashSet<long>();

		private string m_lastSyncedGrudgeString = null;

		private readonly Dictionary<long, float> m_outOfRangeTimers = new Dictionary<long, float>();

		private static ZDOID s_cachedBukeperryZDOID = ZDOID.None;

		public Humanoid Humanoid { get; private set; }

		public MonsterAI MonsterAI { get; private set; }

		private void Awake()
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			if (Instances.Count > 0 && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				BukeperryPlugin.Log.LogWarning((object)"Duplicate Bukeperry detected! Destroying extra instance.");
				ZNetView val = default(ZNetView);
				if (((Component)this).TryGetComponent<ZNetView>(ref val) && val.GetZDO() != null)
				{
					val.ClaimOwnership();
					val.Destroy();
				}
				else
				{
					Object.Destroy((Object)(object)((Component)this).gameObject);
				}
				return;
			}
			Instances.Add(this);
			Humanoid = ((Component)this).GetComponent<Humanoid>();
			MonsterAI = ((Component)this).GetComponent<MonsterAI>();
			m_nview = ((Component)this).GetComponent<ZNetView>();
			float num = default(float);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(((Component)this).transform.position, ref num) && (((Component)this).transform.position.y < num - 1f || ((Component)this).transform.position.y > num + 50f))
			{
				((Component)this).transform.position = new Vector3(((Component)this).transform.position.x, num, ((Component)this).transform.position.z);
			}
		}

		private void Start()
		{
			if ((Object)(object)m_nview == (Object)null)
			{
				m_nview = ((Component)this).GetComponent<ZNetView>();
			}
			if (!((Object)(object)m_nview != (Object)null) || !m_nview.IsValid() || !m_nview.IsOwner() || !((Object)(object)ZNet.instance != (Object)null))
			{
				return;
			}
			float num = m_nview.GetZDO().GetFloat("Bukeperry_LastAttackTime", 0f);
			if (num > 0f)
			{
				double timeSeconds = ZNet.instance.GetTimeSeconds();
				if (timeSeconds - (double)num > 30.0)
				{
					m_nview.GetZDO().Set("Bukeperry_Grudges", "");
					m_nview.GetZDO().Set("Bukeperry_LastAttackTime", 0f);
					m_hostilePlayerIDs.Clear();
					m_lastSyncedGrudgeString = "";
					BukeperryPlugin.Log.LogInfo((object)"Bukeperry woke up after attackers fled while dormant. Chill again.");
				}
			}
		}

		private void OnDestroy()
		{
			Instances.Remove(this);
		}

		public HashSet<long> GetHostilePlayerIDs()
		{
			if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid() || m_nview.GetZDO() == null)
			{
				return m_hostilePlayerIDs;
			}
			string text = m_nview.GetZDO().GetString("Bukeperry_Grudges", "");
			if (text != m_lastSyncedGrudgeString)
			{
				m_lastSyncedGrudgeString = text;
				m_hostilePlayerIDs.Clear();
				if (!string.IsNullOrEmpty(text))
				{
					string[] array = text.Split(',');
					string[] array2 = array;
					foreach (string text2 in array2)
					{
						if (long.TryParse(text2.Trim(), out var result))
						{
							m_hostilePlayerIDs.Add(result);
						}
					}
				}
			}
			return m_hostilePlayerIDs;
		}

		private void Update()
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_nview == (Object)null || !m_nview.IsOwner())
			{
				return;
			}
			HashSet<long> hostilePlayerIDs = GetHostilePlayerIDs();
			if (hostilePlayerIDs.Count == 0)
			{
				return;
			}
			List<long> list = null;
			List<long> list2 = null;
			foreach (long item in hostilePlayerIDs)
			{
				Player player = Player.GetPlayer(item);
				if ((Object)(object)player != (Object)null && ((Character)player).IsDead())
				{
					if (list == null)
					{
						list = new List<long>();
					}
					list.Add(item);
				}
				else if ((Object)(object)player == (Object)null || Vector3.Distance(((Component)this).transform.position, ((Component)player).transform.position) > 50f)
				{
					m_outOfRangeTimers.TryGetValue(item, out var value);
					value += Time.deltaTime;
					m_outOfRangeTimers[item] = value;
					if (value >= 10f)
					{
						if (list2 == null)
						{
							list2 = new List<long>();
						}
						list2.Add(item);
					}
				}
				else
				{
					m_outOfRangeTimers[item] = 0f;
				}
			}
			if (list != null)
			{
				foreach (long item2 in list)
				{
					RemoveGrudge(item2);
				}
			}
			if (list2 == null)
			{
				return;
			}
			foreach (long item3 in list2)
			{
				RemoveGrudge(item3, "fled");
			}
		}

		public void AddGrudge(long playerID)
		{
			if (playerID == 0 || ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && !m_nview.IsOwner()))
			{
				return;
			}
			GetHostilePlayerIDs();
			m_outOfRangeTimers.Remove(playerID);
			if ((Object)(object)ZNet.instance != (Object)null && (Object)(object)m_nview != (Object)null && m_nview.IsValid())
			{
				m_nview.GetZDO().Set("Bukeperry_LastAttackTime", (float)ZNet.instance.GetTimeSeconds());
			}
			if (m_hostilePlayerIDs.Add(playerID))
			{
				string text = (m_lastSyncedGrudgeString = string.Join(",", m_hostilePlayerIDs));
				if ((Object)(object)m_nview != (Object)null && m_nview.IsValid())
				{
					m_nview.GetZDO().Set("Bukeperry_Grudges", text);
				}
				Player player = Player.GetPlayer(playerID);
				string text2 = (((Object)(object)player != (Object)null) ? player.GetPlayerName() : $"Player {playerID}");
				BukeperryPlugin.Log.LogInfo((object)("Bukeperry attacked by " + text2 + "! Entering rage mode."));
			}
			if ((Object)(object)MonsterAI != (Object)null)
			{
				Player player2 = Player.GetPlayer(playerID);
				if ((Object)(object)player2 != (Object)null)
				{
					Traverse.Create((object)MonsterAI).Method("SetTarget", new object[1] { player2 }).GetValue();
				}
				((BaseAI)MonsterAI).Alert();
			}
		}

		public void RemoveGrudge(long playerID, string reason = "died")
		{
			if (playerID == 0 || ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && !m_nview.IsOwner()))
			{
				return;
			}
			GetHostilePlayerIDs();
			m_outOfRangeTimers.Remove(playerID);
			if (!m_hostilePlayerIDs.Remove(playerID))
			{
				return;
			}
			string text = (m_lastSyncedGrudgeString = string.Join(",", m_hostilePlayerIDs));
			if ((Object)(object)m_nview != (Object)null && m_nview.IsValid())
			{
				m_nview.GetZDO().Set("Bukeperry_Grudges", text);
			}
			BukeperryPlugin.Log.LogInfo((object)$"Attacking viking {playerID} {reason}. Grudge resolved.");
			if (m_hostilePlayerIDs.Count == 0)
			{
				if ((Object)(object)m_nview != (Object)null && m_nview.IsValid() && m_nview.GetZDO() != null)
				{
					m_nview.GetZDO().Set("Bukeperry_LastAttackTime", 0f);
				}
				if ((Object)(object)MonsterAI != (Object)null)
				{
					Traverse.Create((object)MonsterAI).Field<Character>("m_targetCreature").Value = null;
					Traverse.Create((object)MonsterAI).Field<bool>("m_alerted").Value = false;
				}
				BukeperryPlugin.Log.LogInfo((object)"All attackers resolved. Bukeperry is chill again.");
			}
		}

		public void OnAttackedBy(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Character)player).IsDead())
			{
				AddGrudge(player.GetPlayerID());
			}
		}

		public bool IsHostileTo(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return GetHostilePlayerIDs().Contains(player.GetPlayerID());
		}

		public static bool TryGetBukeperryLocation(out Vector3 position, out ZDOID zdoid)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			zdoid = ZDOID.None;
			position = Vector3.zero;
			if (Instances.Count > 0 && (Object)(object)Instances[0] != (Object)null)
			{
				BukeperryController bukeperryController = Instances[0];
				position = ((Component)bukeperryController).transform.position;
				ZNetView val = default(ZNetView);
				if (((Component)bukeperryController).TryGetComponent<ZNetView>(ref val) && val.GetZDO() != null)
				{
					zdoid = val.GetZDO().m_uid;
					s_cachedBukeperryZDOID = zdoid;
				}
				return true;
			}
			if (s_cachedBukeperryZDOID != ZDOID.None && ZDOMan.instance != null)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(s_cachedBukeperryZDOID);
				if (zDO != null)
				{
					position = zDO.GetPosition();
					zdoid = s_cachedBukeperryZDOID;
					return true;
				}
			}
			if (ZDOMan.instance != null)
			{
				int stableHashCode = StringExtensionMethods.GetStableHashCode("Bukeperry");
				Dictionary<ZDOID, ZDO> dictionary = Traverse.Create((object)ZDOMan.instance).Field<Dictionary<ZDOID, ZDO>>("m_objectsByID")?.Value;
				if (dictionary != null)
				{
					foreach (KeyValuePair<ZDOID, ZDO> item in dictionary)
					{
						if (item.Value != null && item.Value.GetPrefab() == stableHashCode)
						{
							s_cachedBukeperryZDOID = item.Key;
							zdoid = item.Key;
							position = item.Value.GetPosition();
							return true;
						}
					}
				}
			}
			Vector3? spawnPosition = BukeperrySpawner.GetSpawnPosition();
			if (spawnPosition.HasValue)
			{
				position = spawnPosition.Value;
				return true;
			}
			return false;
		}

		public void Speak(string text)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			ZDOID zdoid = ZDOID.None;
			ZNetView val = default(ZNetView);
			if (((Component)this).TryGetComponent<ZNetView>(ref val) && val.GetZDO() != null)
			{
				zdoid = val.GetZDO().m_uid;
			}
			Speak(zdoid, text);
		}

		public static void Speak(ZDOID zdoid, string text)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrWhiteSpace(text))
			{
				if (ZRoutedRpc.instance != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "BukeperrySpeechRPC", new object[2] { zdoid, text });
				}
				else if ((Object)(object)Chat.instance != (Object)null && Instances.Count > 0)
				{
					Chat.instance.SetNpcText(((Component)Instances[0]).gameObject, Vector3.up * 2.5f, 25f, 7f, "", text, false);
				}
			}
		}

		public static void OnBukeperrySpeechRPC(long sender, ZDOID zdoid, string text)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = null;
			if ((Object)(object)ZNetScene.instance != (Object)null && zdoid != ZDOID.None)
			{
				val = ZNetScene.instance.FindInstance(zdoid);
			}
			if ((Object)(object)val == (Object)null && Instances.Count > 0)
			{
				val = ((Component)Instances[0]).gameObject;
			}
			if ((Object)(object)val != (Object)null && (Object)(object)Chat.instance != (Object)null)
			{
				Chat.instance.SetNpcText(val, Vector3.up * 2.5f, 25f, 7f, "", text, false);
				((Terminal)Chat.instance).AddString("<color=#5599ff>Bukeperry</color>: " + text);
			}
		}
	}
	[BepInPlugin("com.jimbrighter.bukeperrymod", "BukeperryMod", "0.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class BukeperryPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.jimbrighter.bukeperrymod";

		public const string PluginName = "BukeperryMod";

		public const string PluginVersion = "0.3.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<string> ApiEndpointConfig;

		internal static ConfigEntry<string> ApiKeyConfig;

		internal static ConfigEntry<string> ChannelIdConfig;

		internal static ConfigEntry<float> ProximityRadiusConfig;

		private static bool _spawnCheckDone;

		private static float _spawnTimer;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"BukeperryMod v0.3.0 loaded! Bukeperry waking up.");
			ApiEndpointConfig = ((BaseUnityPlugin)this).Config.Bind<string>("ChatApi", "ApiEndpoint", "", "AWS API Gateway endpoint URL for Bukeperry chat.");
			ApiKeyConfig = ((BaseUnityPlugin)this).Config.Bind<string>("ChatApi", "ApiKey", "", "API Key to authenticate with the Bukeperry chat endpoint.");
			ChannelIdConfig = ((BaseUnityPlugin)this).Config.Bind<string>("ChatApi", "ChannelId", "", "Optional channel ID for persistent conversation state. Can be a Discord channel ID for continuity between game and Discord. If blank, conversation is stateless.");
			ProximityRadiusConfig = ((BaseUnityPlugin)this).Config.Bind<float>("ChatApi", "ProximityRadius", 20f, "Maximum distance between player and Bukeperry for chat listening.");
			BukeperryPrefab.Init();
			Harmony.CreateAndPatchAll(typeof(BukeperryPlugin).Assembly, "com.jimbrighter.bukeperrymod");
		}

		private void Update()
		{
			Action result;
			while (BukeperryChatClient.MainThreadQueue.TryDequeue(out result))
			{
				try
				{
					result?.Invoke();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Error in main thread dispatcher: {arg}");
				}
			}
			if (_spawnCheckDone || !((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer() || !((Object)(object)ZoneSystem.instance != (Object)null) || WorldGenerator.instance == null)
			{
				return;
			}
			_spawnTimer += Time.deltaTime;
			if (!(_spawnTimer >= 2f))
			{
				return;
			}
			_spawnTimer = 0f;
			if (ZoneSystem.instance.GetGlobalKey("BukeperrySpawned"))
			{
				_spawnCheckDone = true;
				Log.LogInfo((object)"Bukeperry already active in world. Proximity spawner polling complete.");
				return;
			}
			BukeperrySpawner.CheckAndSpawn();
			if (ZoneSystem.instance.GetGlobalKey("BukeperrySpawned"))
			{
				_spawnCheckDone = true;
			}
		}
	}
	public static class BukeperryPrefab
	{
		public const string PrefabName = "Bukeperry";

		public static void Init()
		{
			PrefabManager.OnVanillaPrefabsAvailable += RegisterPrefab;
		}

		private static void RegisterPrefab()
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Expected O, but got Unknown
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Expected O, but got Unknown
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Expected O, but got Unknown
			if ((Object)(object)PrefabManager.Instance.GetPrefab("Bukeperry") != (Object)null)
			{
				return;
			}
			GameObject prefab = PrefabManager.Instance.GetPrefab("Troll");
			if ((Object)(object)prefab == (Object)null)
			{
				BukeperryPlugin.Log.LogError((object)"Failed to find vanilla 'Troll' prefab!");
				return;
			}
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("Bukeperry", "Troll");
			val.AddComponent<BukeperryController>();
			Trader val2 = val.AddComponent<Trader>();
			val2.m_name = "Bukeperry";
			val2.m_hoverOffset = 3f;
			val2.m_dialogHeight = 6f;
			val2.m_items = new List<TradeItem>();
			GameObject prefab2 = PrefabManager.Instance.GetPrefab("Stone");
			GameObject prefab3 = PrefabManager.Instance.GetPrefab("Wood");
			if ((Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent<ItemDrop>() != (Object)null)
			{
				val2.m_items.Add(new TradeItem
				{
					m_prefab = prefab2.GetComponent<ItemDrop>(),
					m_stack = 1,
					m_price = 1
				});
			}
			if ((Object)(object)prefab3 != (Object)null && (Object)(object)prefab3.GetComponent<ItemDrop>() != (Object)null)
			{
				val2.m_items.Add(new TradeItem
				{
					m_prefab = prefab3.GetComponent<ItemDrop>(),
					m_stack = 1,
					m_price = 10000
				});
			}
			Humanoid component = val.GetComponent<Humanoid>();
			((Character)component).m_name = "Bukeperry";
			((Character)component).m_health = 15000f;
			MonsterAI component2 = val.GetComponent<MonsterAI>();
			if ((Object)(object)component2 != (Object)null)
			{
				((BaseAI)component2).m_aggravatable = true;
				((BaseAI)component2).m_avoidWater = true;
				((BaseAI)component2).m_randomMoveRange = 15f;
			}
			GameObject val3 = null;
			if (component.m_randomSets != null)
			{
				ItemSet[] randomSets = component.m_randomSets;
				foreach (ItemSet val4 in randomSets)
				{
					GameObject[] items = val4.m_items;
					foreach (GameObject val5 in items)
					{
						if ((Object)(object)val5 != (Object)null && ((Object)val5).name.ToLower().Contains("log"))
						{
							val3 = val5;
							break;
						}
					}
					if ((Object)(object)val3 != (Object)null)
					{
						break;
					}
				}
			}
			if ((Object)(object)val3 != (Object)null)
			{
				GameObject val6 = PrefabManager.Instance.CreateClonedPrefab("Bukeperry_Log", ((Object)val3).name);
				ItemDrop component3 = val6.GetComponent<ItemDrop>();
				if ((Object)(object)component3 != (Object)null)
				{
					component3.m_itemData.m_shared.m_damages.m_blunt = 1000f;
					component3.m_itemData.m_shared.m_damages.m_chop = 1000f;
				}
				ItemManager.Instance.AddItem(new CustomItem(val6, true));
				component.m_defaultItems = (GameObject[])(object)new GameObject[1] { val6 };
			}
			else
			{
				BukeperryPlugin.Log.LogWarning((object)"Could not find vanilla log weapon in Troll item sets! Using default items.");
			}
			component.m_randomWeapon = Array.Empty<GameObject>();
			component.m_randomSets = Array.Empty<ItemSet>();
			CreatureConfig val7 = new CreatureConfig
			{
				Name = "Bukeperry",
				Faction = (Faction)2
			};
			CreatureManager.Instance.AddCreature(new CustomCreature(val, true, val7));
			BukeperryPlugin.Log.LogInfo((object)"Bukeperry prefab registered successfully with 15,000 HP and 1,000 DMG.");
		}
	}
	public static class BukeperrySpawner
	{
		public const string GlobalKeyName = "BukeperrySpawned";

		private static bool s_isSpawning;

		private static bool s_locationLogged;

		private static Vector3? s_cachedSpawnPos;

		public static Vector3? GetSpawnPosition()
		{
			if (!s_cachedSpawnPos.HasValue)
			{
				s_cachedSpawnPos = FindNearestBlackForest();
			}
			return s_cachedSpawnPos;
		}

		public static void LogMarkedLocation()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!s_locationLogged && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				Vector3? spawnPosition = GetSpawnPosition();
				if (!spawnPosition.HasValue)
				{
					BukeperryPlugin.Log.LogError((object)"Could not find a valid Black Forest location for Bukeperry.");
					return;
				}
				s_locationLogged = true;
				Vector3 value = spawnPosition.Value;
				float num = Vector3.Distance(Vector3.zero, value);
				BukeperryPlugin.Log.LogInfo((object)$"Bukeperry territory marked in Black Forest at ({value.x:F1}, {value.y:F1}, {value.z:F1}) ({num:F0}m from world spawn).");
				BukeperryPlugin.Log.LogInfo((object)"He will spawn natively when a player approaches within 80m.");
				BukeperryPlugin.Log.LogInfo((object)$"Teleport command for testing: goto {value.x:F0} {value.z:F0}");
			}
		}

		public static void CheckAndSpawn()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZoneSystem.instance == (Object)null || WorldGenerator.instance == null || s_isSpawning || ZoneSystem.instance.GetGlobalKey("BukeperrySpawned"))
			{
				return;
			}
			if (BukeperryController.Instances.Count > 0)
			{
				ZoneSystem.instance.SetGlobalKey("BukeperrySpawned");
				return;
			}
			Vector3? spawnPosition = GetSpawnPosition();
			if (!spawnPosition.HasValue)
			{
				return;
			}
			Vector3 value = spawnPosition.Value;
			if (!ZoneSystem.instance.IsZoneLoaded(value))
			{
				return;
			}
			Player closestPlayer = Player.GetClosestPlayer(value, 80f);
			if ((Object)(object)closestPlayer == (Object)null)
			{
				return;
			}
			s_isSpawning = true;
			try
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab("Bukeperry");
				if ((Object)(object)prefab == (Object)null)
				{
					BukeperryPlugin.Log.LogError((object)"Prefab 'Bukeperry' not found in PrefabManager.");
					return;
				}
				float num = value.y;
				float num2 = default(float);
				if (ZoneSystem.instance.FindFloor(value, ref num2))
				{
					num = num2;
				}
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(value.x, num, value.z);
				ZoneSystem.instance.SetGlobalKey("BukeperrySpawned");
				GameObject val2 = Object.Instantiate<GameObject>(prefab, val, Quaternion.identity);
				BaseAI component = val2.GetComponent<BaseAI>();
				if ((Object)(object)component != (Object)null)
				{
					component.SetPatrolPoint();
				}
				float num3 = Vector3.Distance(Vector3.zero, val);
				BukeperryPlugin.Log.LogInfo((object)$"Player {closestPlayer.GetPlayerName()} approached Black Forest grove! Bukeperry spawned natively at {val} ({num3:F0}m from world spawn).");
			}
			finally
			{
				s_isSpawning = false;
			}
		}

		private static Vector3? FindNearestBlackForest()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Invalid comparison between Unknown and I4
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (WorldGenerator.instance == null)
			{
				return null;
			}
			float num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f);
			Vector3 val = default(Vector3);
			for (float num2 = 100f; num2 <= 5000f; num2 += 50f)
			{
				for (float num3 = 0f; num3 < 360f; num3 += 10f)
				{
					float num4 = num3 * (MathF.PI / 180f);
					float num5 = num2 * Mathf.Cos(num4);
					float num6 = num2 * Mathf.Sin(num4);
					((Vector3)(ref val))..ctor(num5, 0f, num6);
					Biome biome = WorldGenerator.instance.GetBiome(val);
					if ((int)biome == 8)
					{
						float height = WorldGenerator.instance.GetHeight(num5, num6);
						if (height > num + 5f)
						{
							return new Vector3(num5, height, num6);
						}
					}
				}
			}
			return null;
		}
	}
}