Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BukeperryMod v0.3.0
BukeperryMod.dll
Decompiled a week agousing 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; } } }