Please disclose if your mod was created primarily 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 Dogshotgun v1.1.0
Dogshotgun.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Dogshotgun")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("Dogshotgun")] [assembly: AssemblyCopyright("Copyright © Microsoft 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("07688e6d-693d-4872-a4ba-587185507a46")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace DogShotgun; [BepInPlugin("stt.DogShotgun", "DogShotgun", "1.1.0")] public class DogShotgunMod : BaseUnityPlugin { private Harmony _harmony; public static ConfigEntry<bool> EnableMod { get; private set; } public static ConfigEntry<float> SpawnChance { get; private set; } public static ConfigEntry<bool> EnableAttackFixes { get; private set; } public static ConfigEntry<bool> EnablePathingFixes { get; private set; } public static ConfigEntry<int> SpawnCount { get; private set; } public static ConfigEntry<bool> ClearPreviousDogs { get; private set; } private void Awake() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "Enable or disable the mod"); SpawnChance = ((BaseUnityPlugin)this).Config.Bind<float>("Shooting", "SpawnChance", 0.5f, new ConfigDescription("Chance to spawn a dog when shooting (0.0 - 1.0)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); EnableAttackFixes = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "EnableAttackFixes", true, "Fix dog attack behavior (collision, lunge, etc.)"); EnablePathingFixes = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "EnablePathingFixes", true, "Improve dog pathfinding and prevent getting stuck"); SpawnCount = ((BaseUnityPlugin)this).Config.Bind<int>("Shooting", "SpawnCount", 1, new ConfigDescription("Number of dogs to spawn each time the gun is fired", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); ClearPreviousDogs = ((BaseUnityPlugin)this).Config.Bind<bool>("Shooting", "ClearPreviousDogs", true, "If true, removes all previously spawned dogs for this player before spawning new ones. If false, new dogs will be added to existing ones."); _harmony = new Harmony("stt.DogShotgun"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"DogShotgun mod loaded, version 1.1.0 (with AI fixes)"); } } internal class PlayerDogData { public List<EnemyAI> SpawnedDogs { get; set; } = new List<EnemyAI>(); public bool IsCleanedUp { get; set; } = false; } internal static class DogAIFixUtils { private static Dictionary<Type, MethodInfo> killMethodCache = new Dictionary<Type, MethodInfo>(); private static Dictionary<Type, FieldInfo> fieldCache = new Dictionary<Type, FieldInfo>(); public static bool CanAttackPlayer(EnemyAI enemy, PlayerControllerB player) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || player.isPlayerDead || !player.isPlayerControlled) { return false; } if ((Object)(object)player.inAnimationWithEnemy != (Object)null) { return false; } Vector3 val = ((Component)enemy).transform.position + Vector3.up; Vector3 val2 = ((Component)player).transform.position + Vector3.up; RaycastHit val3 = default(RaycastHit); return !Physics.Linecast(val, val2, ref val3, StartOfRound.Instance.collidersAndRoomMask, (QueryTriggerInteraction)1); } public static bool TryKillPlayer(EnemyAI enemy, PlayerControllerB player) { try { Type type = ((object)enemy).GetType(); if (!killMethodCache.TryGetValue(type, out var value)) { value = AccessTools.Method(type, "KillPlayerServerRpc", (Type[])null, (Type[])null); killMethodCache[type] = value; } if (value != null) { value.Invoke(enemy, new object[1] { (int)player.playerClientId }); return true; } } catch (Exception arg) { Debug.LogError((object)$"[DogShotgun] Failed to kill player: {arg}"); } return false; } public static T GetFieldValue<T>(EnemyAI enemy, string fieldName) { try { Type type = ((object)enemy).GetType(); if (!fieldCache.TryGetValue(type, out var value)) { value = AccessTools.Field(type, fieldName); if (value != null) { fieldCache[type] = value; } } if (value != null) { return (T)value.GetValue(enemy); } } catch { } return default(T); } public static bool SetFieldValue(EnemyAI enemy, string fieldName, object value) { try { Type type = ((object)enemy).GetType(); FieldInfo fieldInfo = AccessTools.Field(type, fieldName); if (fieldInfo != null) { fieldInfo.SetValue(enemy, value); return true; } } catch { } return false; } public static void Log(string message, bool isError = false) { if (isError) { Debug.LogError((object)("[DogShotgun] " + message)); } else { Debug.Log((object)("[DogShotgun] " + message)); } } } [HarmonyPatch] public static class GunDogPatch { [CompilerGenerated] private sealed class <MonitorDogUntilPlayerDeath>d__6 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public MouthDogAI dog; public PlayerControllerB targetPlayer; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <MonitorDogUntilPlayerDeath>d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; if ((Object)(object)targetPlayer == (Object)null || targetPlayer.isPlayerDead) { DogAIFixUtils.Log("Player died, killing dog automatically"); ((EnemyAI)dog).KillEnemy(true); return false; } break; } if ((Object)(object)dog != (Object)null && (Object)(object)((Component)dog).gameObject != (Object)null && !((EnemyAI)dog).isEnemyDead) { <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly Dictionary<ulong, PlayerDogData> _playerDogs = new Dictionary<ulong, PlayerDogData>(); [HarmonyPrefix] [HarmonyPatch(typeof(ShotgunItem), "ShootGun")] private static void OnShootGun(ShotgunItem __instance) { try { if (NetworkManager.Singleton.IsServer && DogShotgunMod.EnableMod.Value && !(Random.value > DogShotgunMod.SpawnChance.Value)) { PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy; if (!((Object)(object)playerHeldBy == (Object)null)) { SpawnDogForPlayer(playerHeldBy); } } } catch (Exception arg) { Debug.LogError((object)$"[DogShotgun] Exception in OnShootGun: {arg}"); } } private static void SpawnDogForPlayer(PlayerControllerB player) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } if (!_playerDogs.TryGetValue(player.playerClientId, out var value)) { value = new PlayerDogData(); _playerDogs[player.playerClientId] = value; } if (DogShotgunMod.ClearPreviousDogs.Value && value.SpawnedDogs.Count > 0) { foreach (EnemyAI item in value.SpawnedDogs.ToList()) { if ((Object)(object)item != (Object)null && !item.isEnemyDead) { item.KillEnemy(true); } } value.SpawnedDogs.Clear(); } int value2 = DogShotgunMod.SpawnCount.Value; GameObject mouthDogPrefab = GetMouthDogPrefab(); if ((Object)(object)mouthDogPrefab == (Object)null) { Debug.LogWarning((object)"[DogShotgun] Could not find MouthDog prefab"); return; } for (int i = 0; i < value2; i++) { Vector3 pos = ((Component)player).transform.position + ((Component)player).transform.forward * 1f + ((Component)player).transform.right * Random.Range(-1.5f, 1.5f) + Vector3.up * 0.5f; pos = EnsureNavMeshPosition(pos); GameObject val = Object.Instantiate<GameObject>(mouthDogPrefab, pos, Quaternion.identity); NetworkObject component = val.GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { component.Spawn(true); } MouthDogAI component2 = val.GetComponent<MouthDogAI>(); if ((Object)(object)component2 != (Object)null) { ((EnemyAI)component2).targetPlayer = player; value.SpawnedDogs.Add((EnemyAI)(object)component2); value.IsCleanedUp = false; ((MonoBehaviour)RoundManager.Instance).StartCoroutine(MonitorDogUntilPlayerDeath(component2, player)); DogAIFixUtils.Log("Dog spawned for player " + GetPlayerDisplayName(player)); } } if ((Object)(object)HUDManager.Instance != (Object)null && value2 > 0) { string playerDisplayName = GetPlayerDisplayName(player); string text = ((value2 == 1) ? ("<color=orange>" + playerDisplayName + "'s gunshot attracted a guard dog!</color>") : $"<color=orange>{playerDisplayName}'s gunshot attracted {value2} guard dogs!</color>"); HUDManager.Instance.AddTextToChatOnServer(text, -1); } } private static Vector3 EnsureNavMeshPosition(Vector3 pos) { //IL_0001: 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) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //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_0035: Unknown result type (might be due to invalid IL or missing references) NavMeshHit val = default(NavMeshHit); if (NavMesh.SamplePosition(pos, ref val, 3f, -1)) { return ((NavMeshHit)(ref val)).position + Vector3.up * 0.1f; } return pos; } private static GameObject GetMouthDogPrefab() { EnemyType[] array = Resources.FindObjectsOfTypeAll<EnemyType>(); EnemyType[] array2 = array; foreach (EnemyType val in array2) { if (val.enemyName == "MouthDog" && (Object)(object)val.enemyPrefab != (Object)null) { return val.enemyPrefab; } } EnemyType[] array3 = array; foreach (EnemyType val2 in array3) { if (val2.enemyName.ToLower().Contains("mouthdog") && (Object)(object)val2.enemyPrefab != (Object)null) { return val2.enemyPrefab; } } return null; } private static string GetPlayerDisplayName(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return "Unknown player"; } string playerUsername = player.playerUsername; return (playerUsername.Length > 8) ? (playerUsername.Substring(0, 6) + "..") : playerUsername; } [IteratorStateMachine(typeof(<MonitorDogUntilPlayerDeath>d__6))] private static IEnumerator MonitorDogUntilPlayerDeath(MouthDogAI dog, PlayerControllerB targetPlayer) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <MonitorDogUntilPlayerDeath>d__6(0) { dog = dog, targetPlayer = targetPlayer }; } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] private static void OnPlayerKilled(PlayerControllerB __instance) { try { if (!NetworkManager.Singleton.IsServer) { return; } ulong playerClientId = __instance.playerClientId; if (!_playerDogs.TryGetValue(playerClientId, out var value) || value.IsCleanedUp) { return; } value.IsCleanedUp = true; foreach (EnemyAI item in value.SpawnedDogs.ToList()) { if ((Object)(object)item != (Object)null && !item.isEnemyDead) { item.KillEnemy(true); } } value.SpawnedDogs.Clear(); _playerDogs.Remove(playerClientId); } catch (Exception arg) { Debug.LogError((object)$"[DogShotgun] Exception in OnPlayerKilled: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(EnemyAI), "KillEnemy")] private static void OnDogKilled(EnemyAI __instance) { try { if (!NetworkManager.Singleton.IsServer || !(__instance is MouthDogAI)) { return; } foreach (KeyValuePair<ulong, PlayerDogData> item in _playerDogs.ToList()) { if (item.Value.SpawnedDogs.Contains(__instance)) { item.Value.SpawnedDogs.Remove(__instance); if (item.Value.SpawnedDogs.Count == 0 && item.Value.IsCleanedUp) { _playerDogs.Remove(item.Key); } break; } } } catch (Exception arg) { Debug.LogError((object)$"[DogShotgun] Exception in OnDogKilled: {arg}"); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] private static void OnDisconnect() { _playerDogs.Clear(); } } [HarmonyPatch(typeof(MouthDogAI), "OnCollideWithPlayer")] public static class MouthDogAI_CollisionPatch { [HarmonyPrefix] public static bool Prefix(MouthDogAI __instance, ref Collider other) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) if (!DogShotgunMod.EnableMod.Value || !DogShotgunMod.EnableAttackFixes.Value) { return true; } if ((Object)(object)RoundManager.Instance == (Object)null || !((NetworkBehaviour)RoundManager.Instance).IsHost) { return true; } try { PlayerControllerB component = ((Component)other).gameObject.GetComponent<PlayerControllerB>(); if (!DogAIFixUtils.CanAttackPlayer((EnemyAI)(object)__instance, component)) { return true; } bool fieldValue = DogAIFixUtils.GetFieldValue<bool>((EnemyAI)(object)__instance, "inKillAnimation"); bool fieldValue2 = DogAIFixUtils.GetFieldValue<bool>((EnemyAI)(object)__instance, "inLunge"); if (fieldValue) { return true; } if (((EnemyAI)__instance).currentBehaviourStateIndex == 3) { component.inAnimationWithEnemy = (EnemyAI)(object)__instance; return !DogAIFixUtils.TryKillPlayer((EnemyAI)(object)__instance, component); } if (((EnemyAI)__instance).currentBehaviourStateIndex == 0 || ((EnemyAI)__instance).currentBehaviourStateIndex == 1) { ((EnemyAI)__instance).SwitchToBehaviourState(2); ((EnemyAI)__instance).SetDestinationToPosition(((Component)component).transform.position, false); return false; } if (((EnemyAI)__instance).currentBehaviourStateIndex == 2 && !fieldValue2) { ((Component)__instance).transform.LookAt(((Component)component).transform.position); ((Component)__instance).transform.localEulerAngles = new Vector3(0f, ((Component)__instance).transform.eulerAngles.y, 0f); EnterLunge(__instance); return false; } } catch (Exception arg) { DogAIFixUtils.Log($"MouthDog attack fix exception: {arg}", isError: true); } return true; } private static void EnterLunge(MouthDogAI instance) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0084: Unknown result type (might be due to invalid IL or missing references) try { ((EnemyAI)instance).SwitchToBehaviourState(3); Ray val = default(Ray); ((Ray)(ref val))..ctor(((Component)instance).transform.position + Vector3.up, ((Component)instance).transform.forward); RaycastHit val2 = default(RaycastHit); Vector3 val3 = ((!Physics.Raycast(val, ref val2, 17f, StartOfRound.Instance.collidersAndRoomMask)) ? ((Ray)(ref val)).GetPoint(17f) : ((RaycastHit)(ref val2)).point); val3 = RoundManager.Instance.GetNavMeshPosition(val3, default(NavMeshHit), 5f, -1); ((EnemyAI)instance).SetDestinationToPosition(val3, false); ((EnemyAI)instance).agent.speed = 13f; } catch (Exception arg) { DogAIFixUtils.Log($"MouthDog lunge exception: {arg}", isError: true); } } } [HarmonyPatch(typeof(MouthDogAI))] public static class MouthDogAI_PathingPatch { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix(MouthDogAI __instance) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!DogShotgunMod.EnableMod.Value || !DogShotgunMod.EnablePathingFixes.Value) { return; } try { if ((Object)(object)((EnemyAI)__instance).agent != (Object)null) { ((EnemyAI)__instance).agent.radius = 0.7f; ((EnemyAI)__instance).agent.height = 1.5f; ((EnemyAI)__instance).agent.autoRepath = true; ((EnemyAI)__instance).agent.stoppingDistance = 1f; NavMeshHit val = default(NavMeshHit); if (!((EnemyAI)__instance).agent.isOnNavMesh && NavMesh.SamplePosition(((Component)__instance).transform.position, ref val, 10f, -1)) { ((EnemyAI)__instance).agent.Warp(((NavMeshHit)(ref val)).position); } } DogAIFixUtils.Log("Applied pathing fixes for dog"); } catch (Exception arg) { DogAIFixUtils.Log($"Error in dog pathing fix: {arg}", isError: true); } } [HarmonyPostfix] [HarmonyPatch("DoAIInterval")] private static void DoAIIntervalPostfix(MouthDogAI __instance) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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) if (!DogShotgunMod.EnableMod.Value || !DogShotgunMod.EnablePathingFixes.Value) { return; } try { if ((Object)(object)((EnemyAI)__instance).agent != (Object)null && ((EnemyAI)__instance).agent.hasPath) { Vector3 velocity = ((EnemyAI)__instance).agent.velocity; if (((Vector3)(ref velocity)).magnitude < 0.1f) { ((EnemyAI)__instance).agent.ResetPath(); ((EnemyAI)__instance).SetDestinationToPosition(((Component)__instance).transform.position + Random.insideUnitSphere * 5f, false); } } } catch (Exception arg) { DogAIFixUtils.Log($"Error in dog DoAIInterval: {arg}", isError: true); } } } [HarmonyPatch(typeof(EnemyAI), "PlayerIsTargetable")] public static class PlayerIsTargetablePatch { [HarmonyPrefix] private static void Prefix(PlayerControllerB playerScript, ref bool __result, ref bool overrideInsideFactoryCheck) { if (DogShotgunMod.EnableMod.Value) { overrideInsideFactoryCheck = true; } } }