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 DangerGift v1.0.0
DangerGift.dll
Decompiled 3 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DangerGift")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DangerGift")] [assembly: AssemblyTitle("DangerGift")] [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 DangerGift { internal static class ConfigSettings { public static ConfigEntry<bool> Enabled; public static ConfigEntry<float> SpawnChance; public static ConfigEntry<bool> PreventOpeningOnShip; public static ConfigEntry<bool> SpawnMonsters; public static ConfigEntry<bool> SpawnTraps; public static ConfigEntry<float> MonstersSpawnChance; public static ConfigEntry<float> TrapsSpawnChance; public static ConfigEntry<float> SpawnDelay; public static ConfigEntry<string> EnemyBlocklist; public static ConfigEntry<bool> IsAllowlist; public static ConfigEntry<string> EnemyAllowlist; public static ConfigEntry<bool> AllowOutsideSpawnInside; public static ConfigEntry<bool> AllowInsideSpawnOutside; public static ConfigEntry<bool> IgnoreInsideOutsideRule; public static ConfigEntry<bool> SpawnMines; public static ConfigEntry<bool> SpawnTurrets; public static ConfigEntry<bool> SpawnSpikeTraps; public static ConfigEntry<string> MoonsToIgnore; public static ConfigEntry<bool> EnableLogging; public static HashSet<string> BlocklistSet { get; private set; } public static HashSet<string> AllowlistSet { get; private set; } public static HashSet<string> IgnoredMoonsSet { get; private set; } public static void Init(ConfigFile cfg) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown Enabled = cfg.Bind<bool>("General", "Enabled", true, "Enable or disable the whole mod."); SpawnChance = cfg.Bind<float>("General", "SpawnChance", 0.15f, new ConfigDescription("Base chance (0.0 - 1.0) that opening a Present spawns a monster/trap instead of loot.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); PreventOpeningOnShip = cfg.Bind<bool>("General", "PreventOpeningOnShip", false, "If true, presents can ONLY be opened while landed on a moon and standing outside the ship (on the surface or in the facility). Opening is blocked aboard the ship and while in orbit / after take-off (not on a moon). The present is not consumed — it can still be opened later in a valid spot."); SpawnMonsters = cfg.Bind<bool>("Spawning", "SpawnMonsters", true, "Allow presents to spawn monsters."); SpawnTraps = cfg.Bind<bool>("Spawning", "SpawnTraps", true, "Allow presents to spawn traps."); MonstersSpawnChance = cfg.Bind<float>("Spawning", "MonstersSpawnChance", -1f, new ConfigDescription("Separate chance for monsters. Set to -1 to just use the general SpawnChance. If either this or TrapsSpawnChance is >= 0, monsters and traps are rolled independently.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>())); TrapsSpawnChance = cfg.Bind<float>("Spawning", "TrapsSpawnChance", -1f, new ConfigDescription("Separate chance for traps. Set to -1 to just use the general SpawnChance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>())); SpawnDelay = cfg.Bind<float>("Spawning", "SpawnDelay", 0.7f, new ConfigDescription("Delay, in seconds, between opening the present and the entity actually appearing. Gives players a brief window to react.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); EnemyBlocklist = cfg.Bind<string>("Monsters", "EnemyBlocklist", "", "Comma-separated internal enemy names that must NEVER spawn (used when IsAllowlist = false). Example: \"Flowerman,SandSpider\". Enable logging to print the exact internal names."); IsAllowlist = cfg.Bind<bool>("Monsters", "IsAllowlist", true, "If true, ONLY monsters in EnemyAllowlist can spawn. If false, all monsters EXCEPT those in EnemyBlocklist can spawn."); EnemyAllowlist = cfg.Bind<string>("Monsters", "EnemyAllowlist", "Manticoil,RoamingLocust,RedLocust,HoardingBug,GunkFish,Slime,TulipSnake,Maneater", "Comma-separated internal enemy names allowed when IsAllowlist = true. Names are matched leniently (case/space insensitive) against the game's enemy names."); AllowOutsideSpawnInside = cfg.Bind<bool>("Monsters", "AllowOutsideSpawnInside", false, "If true, outdoor monsters may also spawn from presents opened inside the facility. Off by default because outdoor AI can misbehave on the indoor navmesh."); AllowInsideSpawnOutside = cfg.Bind<bool>("Monsters", "AllowInsideSpawnOutside", false, "If true, indoor monsters may also spawn from presents opened outside."); IgnoreInsideOutsideRule = cfg.Bind<bool>("Monsters", "IgnoreInsideOutsideRule", false, "If true, the inside/outside rule is ignored entirely: BOTH facility (indoor) and outdoor monsters can spawn no matter where the present is opened. Overrides AllowOutsideSpawnInside / AllowInsideSpawnOutside."); SpawnMines = cfg.Bind<bool>("Traps", "SpawnMines", true, "Allow landmines to be spawned from presents."); SpawnTurrets = cfg.Bind<bool>("Traps", "SpawnTurrets", false, "Allow turrets to be spawned from presents."); SpawnSpikeTraps = cfg.Bind<bool>("Traps", "SpawnSpikeTraps", false, "Allow spike roof traps to be spawned. EXPERIMENTAL: these are designed to hang from ceilings, so a floor-spawned one may look/behave oddly."); MoonsToIgnore = cfg.Bind<string>("Moons", "MoonsToIgnore", "", "Comma-separated moon names on which the mod is DISABLED. Matched against the planet name, so \"Titan\" matches \"8 Titan\". Leave empty to run on every moon."); EnableLogging = cfg.Bind<bool>("Debug", "EnableLogging", false, "Verbose logging to the BepInEx console (also prints every available enemy/trap name)."); RebuildCaches(); EnemyBlocklist.SettingChanged += delegate { RebuildCaches(); }; EnemyAllowlist.SettingChanged += delegate { RebuildCaches(); }; MoonsToIgnore.SettingChanged += delegate { RebuildCaches(); }; } private static void RebuildCaches() { BlocklistSet = ParseList(EnemyBlocklist.Value); AllowlistSet = ParseList(EnemyAllowlist.Value); IgnoredMoonsSet = ParseList(MoonsToIgnore.Value); } private static HashSet<string> ParseList(string raw) { HashSet<string> hashSet = new HashSet<string>(); if (string.IsNullOrWhiteSpace(raw)) { return hashSet; } string[] array = raw.Split(','); for (int i = 0; i < array.Length; i++) { string text = Normalize(array[i]); if (text.Length > 0) { hashSet.Add(text); } } return hashSet; } public static string Normalize(string s) { if (string.IsNullOrEmpty(s)) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(s.Length); foreach (char c in s) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(GiftBoxItem))] internal static class GiftBoxPatches { private static readonly FieldInfo ExecStageField = typeof(NetworkBehaviour).GetField("__rpc_exec_stage", BindingFlags.Instance | BindingFlags.NonPublic); [HarmonyPatch("ItemActivate", new Type[] { typeof(bool), typeof(bool) })] [HarmonyPrefix] private static bool ItemActivatePrefix(GiftBoxItem __instance, bool buttonDown) { try { if (!ConfigSettings.Enabled.Value) { return true; } if (!ConfigSettings.PreventOpeningOnShip.Value) { return true; } if (CanOpenHere(__instance)) { return true; } if (buttonDown) { ShowCannotOpenTip(); } return false; } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemActivate prefix error: {arg}"); return true; } } [HarmonyPatch("OpenGiftBoxServerRpc")] [HarmonyPrefix] private static bool OpenGiftBoxPrefix(GiftBoxItem __instance) { try { if (!ConfigSettings.Enabled.Value) { return true; } if (IsIgnoredMoon()) { return true; } if (!IsServerExecutionStage((NetworkBehaviour)(object)__instance)) { return true; } if (ConfigSettings.PreventOpeningOnShip.Value && !CanOpenHere(__instance)) { return true; } return !PresentReplacementManager.TryReplace(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"OpenGiftBox prefix error: {arg}"); return true; } } private static bool CanOpenHere(GiftBoxItem box) { if (ShipHasLanded()) { return !IsHolderInShip(box); } return false; } private static bool IsHolderInShip(GiftBoxItem box) { if ((Object)(object)((GrabbableObject)box).playerHeldBy != (Object)null) { return ((GrabbableObject)box).playerHeldBy.isInHangarShipRoom; } return false; } private static bool ShipHasLanded() { if ((Object)(object)StartOfRound.Instance != (Object)null) { return StartOfRound.Instance.shipHasLanded; } return false; } private static void ShowCannotOpenTip() { try { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip("Present", "Presents can only be opened out on the moon.", false, false, "LC_Tip1"); } } catch { } } private static bool IsServerExecutionStage(NetworkBehaviour nb) { if (ExecStageField == null) { return nb.IsServer; } try { object value = ExecStageField.GetValue(nb); string text = value.ToString(); if (text == "Execute" || text == "Server") { return true; } return Convert.ToInt32(value) == 1; } catch { return nb.IsServer; } } internal static void SetRpcExecStageToSend(NetworkBehaviour nb) { if (ExecStageField == null) { return; } try { ExecStageField.SetValue(nb, Enum.ToObject(ExecStageField.FieldType, 0)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not reset RPC exec stage: " + ex.Message)); } } private static bool IsIgnoredMoon() { HashSet<string> ignoredMoonsSet = ConfigSettings.IgnoredMoonsSet; if (ignoredMoonsSet == null || ignoredMoonsSet.Count == 0) { return false; } SelectableLevel val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.currentLevel : null); if ((Object)(object)val == (Object)null) { return false; } string text = ConfigSettings.Normalize(val.PlanetName); foreach (string item in ignoredMoonsSet) { if (text.Contains(item)) { return true; } } return false; } } [HarmonyPatch(typeof(StartOfRound))] internal static class RoundResetPatch { [HarmonyPatch("StartGame")] [HarmonyPostfix] private static void OnStartGame() { PresentReplacementManager.Reset(); } } [BepInPlugin("Timofey.DangerGift", "DangerGift", "1.0.0")] [BepInProcess("Lethal Company.exe")] public class Plugin : BaseUnityPlugin { public const string GUID = "Timofey.DangerGift"; public const string NAME = "DangerGift"; public const string VERSION = "1.0.0"; private readonly Harmony _harmony = new Harmony("Timofey.DangerGift"); public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigSettings.Init(((BaseUnityPlugin)this).Config); _harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"DangerGift v1.0.0 loaded. Some presents bite back."); } } internal static class PresentReplacementManager { private class SpawnJob { public bool IsMonster; public EnemyType Enemy; public SpawnHelper.TrapTarget Trap; public Vector3 Position; } private static readonly Dictionary<ulong, bool> _decisions = new Dictionary<ulong, bool>(); private static readonly Random Rng = new Random(); public static bool TryReplace(GiftBoxItem box) { ulong networkObjectId = ((NetworkBehaviour)box).NetworkObjectId; if (_decisions.TryGetValue(networkObjectId, out var value)) { return value; } bool flag; try { flag = Decide(box); } catch (Exception arg) { Plugin.Log.LogError((object)$"Present replacement failed, falling back to normal loot: {arg}"); flag = false; } _decisions[networkObjectId] = flag; return flag; } private static bool Decide(GiftBoxItem box) { //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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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) bool isInFactory = ((GrabbableObject)box).isInFactory; Vector3 position = ((Component)box).transform.position; RollCategories(out var monster, out var trap); if (!monster && !trap) { return false; } List<SpawnJob> list = new List<SpawnJob>(2); if (monster && ConfigSettings.SpawnMonsters.Value) { EnemyType val = SpawnHelper.ResolveMonster(isInFactory); if ((Object)(object)val != (Object)null && SpawnHelper.TryGetMonsterPosition(position, out var position2)) { list.Add(new SpawnJob { IsMonster = true, Enemy = val, Position = position2 }); } } if (trap && ConfigSettings.SpawnTraps.Value) { SpawnHelper.TrapTarget trapTarget = SpawnHelper.ResolveTrap(position); if (trapTarget != null) { list.Add(new SpawnJob { IsMonster = false, Trap = trapTarget, Position = trapTarget.Position }); } } if (list.Count == 0) { return false; } SpawnJob job = list[Rng.Next(list.Count)]; OpenBoxWithoutLoot(box); if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(SpawnAfterDelay(job)); } else { ExecuteJob(job); } return true; } private static void OpenBoxWithoutLoot(GiftBoxItem box) { try { GiftBoxPatches.SetRpcExecStageToSend((NetworkBehaviour)(object)box); box.OpenGiftBoxNoPresentClientRpc(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to open present cleanly: {arg}"); } } private static void RollCategories(out bool monster, out bool trap) { monster = false; trap = false; bool flag = ConfigSettings.MonstersSpawnChance.Value >= 0f; bool flag2 = ConfigSettings.TrapsSpawnChance.Value >= 0f; float value = ConfigSettings.SpawnChance.Value; if (!flag && !flag2) { if (Rng.NextDouble() >= (double)value) { return; } bool value2 = ConfigSettings.SpawnMonsters.Value; bool value3 = ConfigSettings.SpawnTraps.Value; if (value2 && value3) { if (Rng.NextDouble() < 0.5) { monster = true; } else { trap = true; } } else if (value2) { monster = true; } else if (value3) { trap = true; } } else { float num = ((!ConfigSettings.SpawnMonsters.Value) ? 0f : (flag ? ConfigSettings.MonstersSpawnChance.Value : value)); float num2 = ((!ConfigSettings.SpawnTraps.Value) ? 0f : (flag2 ? ConfigSettings.TrapsSpawnChance.Value : value)); monster = Rng.NextDouble() < (double)num; trap = Rng.NextDouble() < (double)num2; } } private static IEnumerator SpawnAfterDelay(SpawnJob job) { yield return (object)new WaitForSeconds(Mathf.Max(0f, ConfigSettings.SpawnDelay.Value)); ExecuteJob(job); } private static void ExecuteJob(SpawnJob job) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) try { if (job.IsMonster) { SpawnHelper.SpawnMonster(job.Enemy, job.Position); } else { SpawnHelper.SpawnTrap(job.Trap); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to spawn present entity: {arg}"); } } public static void Reset() { _decisions.Clear(); } } internal static class SpawnHelper { public class TrapTarget { public GameObject Prefab; public Vector3 Position; public Quaternion Rotation; } private static readonly Random Rng = new Random(); private static GameObject _minePrefab; private static GameObject _turretPrefab; private static GameObject _spikeTrapPrefab; private static bool _trapsScanned; private static bool _dumpedAllEnemies; public static EnemyType ResolveMonster(bool insideFacility) { List<EnemyType> list = new List<EnemyType>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> list2 = new List<string>(); EnemyType[] array = Resources.FindObjectsOfTypeAll<EnemyType>(); foreach (EnemyType val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.enemyPrefab == (Object)null) && !string.IsNullOrWhiteSpace(val.enemyName) && hashSet.Add(val.enemyName)) { list2.Add(val.enemyName); if (PassesListFilter(val) && PassesInsideOutside(val, insideFacility)) { list.Add(val); } } } if (ConfigSettings.EnableLogging.Value && !_dumpedAllEnemies) { _dumpedAllEnemies = true; list2.Sort(StringComparer.OrdinalIgnoreCase); Plugin.Log.LogInfo((object)string.Format("[Enemies] all loaded ({0}): {1}", list2.Count, string.Join(", ", list2))); } if (list.Count == 0) { if (ConfigSettings.EnableLogging.Value) { Plugin.Log.LogInfo((object)$"[Enemies] inside={insideFacility} allowed=0 (nothing to spawn)"); } return null; } EnemyType val2 = list[Rng.Next(list.Count)]; if (ConfigSettings.EnableLogging.Value) { List<string> list3 = new List<string>(list.Count); foreach (EnemyType item in list) { list3.Add(item.enemyName); } Plugin.Log.LogInfo((object)($"[Enemies] inside={insideFacility} allowed={list.Count} " + "=> [" + string.Join(", ", list3) + "] chosen='" + val2.enemyName + "'")); } return val2; } private static bool PassesInsideOutside(EnemyType et, bool insideFacility) { if (ConfigSettings.IgnoreInsideOutsideRule.Value) { return true; } bool flag = et.isOutsideEnemy || et.isDaytimeEnemy; bool flag2 = !et.isOutsideEnemy && !et.isDaytimeEnemy; if (insideFacility) { if (!flag2) { return ConfigSettings.AllowOutsideSpawnInside.Value; } return true; } if (!flag) { return ConfigSettings.AllowInsideSpawnOutside.Value; } return true; } private static bool PassesListFilter(EnemyType et) { bool value = ConfigSettings.IsAllowlist.Value; HashSet<string> set = (value ? ConfigSettings.AllowlistSet : ConfigSettings.BlocklistSet); bool flag = NameMatchesSet(et, set); if (!value) { return !flag; } return flag; } private static bool NameMatchesSet(EnemyType et, HashSet<string> set) { if (set == null || set.Count == 0) { return false; } List<string> list = new List<string>(3); if (!string.IsNullOrEmpty(et.enemyName)) { list.Add(ConfigSettings.Normalize(et.enemyName)); } if ((Object)(object)et.enemyPrefab != (Object)null) { list.Add(ConfigSettings.Normalize(((Object)et.enemyPrefab).name)); } list.Add(ConfigSettings.Normalize(((Object)et).name)); foreach (string item in set) { foreach (string item2 in ExpandAliases(item)) { if (item2.Length == 0) { continue; } foreach (string item3 in list) { if (item3.Length != 0 && (item3 == item2 || item3.Contains(item2) || item2.Contains(item3))) { return true; } } } } return false; } private static IEnumerable<string> ExpandAliases(string normalized) { yield return normalized; if (normalized == null) { yield break; } switch (normalized.Length) { case 9: switch (normalized[0]) { default: yield break; case 'm': if (normalized == "manticoil") { yield return "doublewing"; } yield break; case 'r': break; } if (!(normalized == "redlocust")) { break; } goto IL_01cd; case 8: switch (normalized[0]) { case 'g': if (normalized == "gunkfish") { yield return "gutfish"; yield return "fish"; } break; case 'm': if (normalized == "maneater") { yield return "cavedweller"; yield return "caveddweller"; } break; } break; case 13: if (!(normalized == "roaminglocust")) { break; } goto IL_01cd; case 11: if (normalized == "hoardingbug") { yield return "hoarderbug"; yield return "hoarding"; } break; case 12: if (normalized == "kidnapperfox") { yield return "bushwolf"; yield return "fox"; } break; case 5: if (normalized == "slime") { yield return "hygrodere"; yield return "blob"; } break; case 10: { if (normalized == "tulipsnake") { yield return "flowersnake"; yield return "snake"; } break; } IL_01cd: yield return "redlocustbees"; yield return "docilelocust"; yield return "locust"; break; } } public static void SpawnMonster(EnemyType enemyType, Vector3 position) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) if (!((Object)(object)RoundManager.Instance == (Object)null) && !((Object)(object)enemyType == (Object)null)) { float num = (float)(Rng.NextDouble() * 360.0); RoundManager.Instance.SpawnEnemyGameObject(position, num, -1, enemyType); if (ConfigSettings.EnableLogging.Value) { Plugin.Log.LogInfo((object)$"[Spawn] Monster '{enemyType.enemyName}' at {position}"); } } } private static void ScanTrapPrefabs() { if (_trapsScanned) { return; } _trapsScanned = true; try { List<SpawnableMapObject[]> list = new List<SpawnableMapObject[]>(); SelectableLevel val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.currentLevel : null); if ((Object)(object)val != (Object)null && val.spawnableMapObjects != null) { list.Add(val.spawnableMapObjects); } SelectableLevel[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.levels : null); if (array != null) { SelectableLevel[] array2 = array; foreach (SelectableLevel val2 in array2) { if ((Object)(object)val2 != (Object)null && val2.spawnableMapObjects != null) { list.Add(val2.spawnableMapObjects); } } } foreach (SpawnableMapObject[] item in list) { for (int i = 0; i < item.Length; i++) { GameObject val3 = item[i]?.prefabToSpawn; if (!((Object)(object)val3 == (Object)null)) { if ((Object)(object)_minePrefab == (Object)null && (Object)(object)val3.GetComponentInChildren<Landmine>() != (Object)null) { _minePrefab = val3; } if ((Object)(object)_turretPrefab == (Object)null && (Object)(object)val3.GetComponentInChildren<Turret>() != (Object)null) { _turretPrefab = val3; } if ((Object)(object)_spikeTrapPrefab == (Object)null && (Object)(object)val3.GetComponentInChildren<SpikeRoofTrap>() != (Object)null) { _spikeTrapPrefab = val3; } } } if ((Object)(object)_minePrefab != (Object)null && (Object)(object)_turretPrefab != (Object)null && (Object)(object)_spikeTrapPrefab != (Object)null) { break; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Trap prefab scan failed: " + ex.Message)); } if (ConfigSettings.EnableLogging.Value) { Plugin.Log.LogInfo((object)$"[Traps] mine={(Object)(object)_minePrefab != (Object)null} turret={(Object)(object)_turretPrefab != (Object)null} spike={(Object)(object)_spikeTrapPrefab != (Object)null}"); } } public static TrapTarget ResolveTrap(Vector3 near) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) ScanTrapPrefabs(); List<GameObject> list = new List<GameObject>(); if (ConfigSettings.SpawnMines.Value && (Object)(object)_minePrefab != (Object)null) { list.Add(_minePrefab); } if (ConfigSettings.SpawnTurrets.Value && (Object)(object)_turretPrefab != (Object)null) { list.Add(_turretPrefab); } if (ConfigSettings.SpawnSpikeTraps.Value && (Object)(object)_spikeTrapPrefab != (Object)null) { list.Add(_spikeTrapPrefab); } if (list.Count == 0) { return null; } if (!TryGetGroundPosition(near, out var position, out var normal)) { return null; } GameObject prefab = list[Rng.Next(list.Count)]; Quaternion rotation = Quaternion.FromToRotation(Vector3.up, normal) * Quaternion.Euler(0f, (float)(Rng.NextDouble() * 360.0), 0f); return new TrapTarget { Prefab = prefab, Position = position, Rotation = rotation }; } public static void SpawnTrap(TrapTarget target) { //IL_0019: 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) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (target == null || (Object)(object)target.Prefab == (Object)null) { return; } GameObject val = Object.Instantiate<GameObject>(target.Prefab, target.Position, target.Rotation); NetworkObject component = val.GetComponent<NetworkObject>(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)("Trap prefab '" + ((Object)target.Prefab).name + "' has no NetworkObject; aborting spawn.")); Object.Destroy((Object)(object)val); return; } component.Spawn(true); if (ConfigSettings.EnableLogging.Value) { Plugin.Log.LogInfo((object)$"[Spawn] Trap '{((Object)target.Prefab).name}' at {target.Position}"); } } public static bool TryGetGroundPosition(Vector3 origin, out Vector3 position, out Vector3 normal) { //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_0008: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_0057: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_0087: Unknown result type (might be due to invalid IL or missing references) position = origin; normal = Vector3.up; RaycastHit val = default(RaycastHit); if (Physics.Raycast(origin + Vector3.up * 0.5f, Vector3.down, ref val, 12f, -1, (QueryTriggerInteraction)1)) { position = ((RaycastHit)(ref val)).point; normal = ((RaycastHit)(ref val)).normal; } NavMeshHit val2 = default(NavMeshHit); if (NavMesh.SamplePosition(position, ref val2, 3f, -1)) { position = ((NavMeshHit)(ref val2)).position + Vector3.up * 0.05f; return true; } return false; } public static bool TryGetMonsterPosition(Vector3 origin, out Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (TryGetGroundPosition(origin, out position, out var _)) { return true; } position = origin; return true; } } }