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 Bestiary Redux v1.0.0
BestiaryRedux.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BestiaryRedux.Compat; using BestiaryRedux.Core; using BestiaryRedux.Enemies.HoarderBug; using BestiaryRedux.Enemies.HoarderBug.Nest; using BestiaryRedux.Enemies.Leviathan; using BestiaryRedux.ItemUse; using BestiaryRedux.Net; using Dawn; using GameNetcodeStuff; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using On; using On.GameNetcodeStuff; using Unity.Collections; using Unity.Jobs; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.SceneManagement; [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("Moroxide")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+faaf570efa8467eb5d63232035660187b9683874")] [assembly: AssemblyProduct("BestiaryRedux")] [assembly: AssemblyTitle("BestiaryRedux")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 BestiaryRedux { [BepInPlugin("com.github.moroxiderblx.bestiaryredux", "BestiaryRedux", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal static Plugin Instance; private void Awake() { Instance = this; Log.Init(((BaseUnityPlugin)this).Logger); OldNames.CopyOldFiles(((BaseUnityPlugin)this).Config); Runner.Create(); ConfigLayout.Begin(((BaseUnityPlugin)this).Config); ConfigLayout.MoveSection("2. Hoarding Bug", "3. Hoarding Bug"); ConfigLayout.MoveSection("2.1 Hoarding Bug Items", "3.1 Hoarding Bug Items"); ConfigLayout.MoveSection("3. Hoarding Bug Nest", "4. Hoarding Bug Nest"); ConfigLayout.MoveSection("4. Earth Leviathan", "2. Earth Leviathan"); ModConfig.Load(); LoadConfig("Earth Leviathan", LeviathanConfig.Load); LoadConfig("Hoarding Bug", HoarderBugConfig.Load); ConfigLayout.Finish(); LobbyCompatibility.Register(); Bundle.Load(); NestChunks.Register(); ModuleRegistry.Add(() => new NetworkModule()); ModuleRegistry.Add(() => new ItemUseModule()); ModuleRegistry.Add(() => new WeaponDamage()); ModuleRegistry.Add(() => new HoarderBugItemUse()); ModuleRegistry.Add(() => new HoarderBugHoldAtNest()); ModuleRegistry.Add(() => new NestModule()); ModuleRegistry.Add(() => new LeviathanModule()); ModuleRegistry.Refresh(); ((BaseUnityPlugin)this).Config.SettingChanged += delegate { if (!ConfigLayout.Busy) { Runner.EndOfFrame("Config refresh", delegate { bool flag = ConfigLayout.Refresh(); ModuleRegistry.Refresh(); NetworkModule.OnConfigChanged(); if (flag) { LethalConfig.ReloadOpenPage(); } }); } }; Runner.EndOfFrame("Symbiosis check", delegate { Symbiosis.Check(); ModuleRegistry.Refresh(); }); Log.Info("com.github.moroxiderblx.bestiaryredux [1.0.0] loaded"); } private static void LoadConfig(string enemy, Action load) { try { load(); } catch (Exception arg) { Log.Error($"Config: the {enemy} section could not be loaded: {arg}"); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "com.github.moroxiderblx.bestiaryredux"; public const string PLUGIN_NAME = "BestiaryRedux"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace BestiaryRedux.Net { internal static class NetChannel { internal delegate void Handler(ulong sender, FastBufferReader reader); internal delegate void Writer(ref FastBufferWriter writer); private const string Prefix = "BestiaryRedux."; private static readonly Dictionary<string, Handler> handlers = new Dictionary<string, Handler>(); private static CustomMessagingManager? Messaging { get { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null) { return null; } return singleton.CustomMessagingManager; } } internal static bool InSession { get { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening) { return Messaging != null; } return false; } } internal static bool IsHost { get { if (InSession) { return NetworkManager.Singleton.IsServer; } return false; } } internal static ulong LocalClientId { get { NetworkManager singleton = NetworkManager.Singleton; if (singleton == null) { return 0uL; } return singleton.LocalClientId; } } internal static ulong HostClientId => 0uL; internal static void On(string name, Handler handler) { handlers["BestiaryRedux." + name] = handler; if (InSession) { Register("BestiaryRedux." + name, handler); } } internal static void RegisterAll() { if (!InSession) { return; } foreach (KeyValuePair<string, Handler> handler in handlers) { Register(handler.Key, handler.Value); } } private static void Register(string fullName, Handler handler) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown Messaging.RegisterNamedMessageHandler(fullName, (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) try { handler(sender, reader); } catch (Exception arg) { Log.Error($"Network: message {fullName} from client {sender} failed: {arg}"); } }); } internal static void SendToHost(string name, Writer write) { if (InSession && !IsHost) { Send(name, write, delegate(CustomMessagingManager m, string full, FastBufferWriter w) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) m.SendNamedMessage(full, HostClientId, w, (NetworkDelivery)3); }); } } internal static void SendTo(string name, IReadOnlyList<ulong> clientIds, Writer write, bool large = false) { if (IsHost && clientIds.Count != 0) { Send(name, write, delegate(CustomMessagingManager m, string full, FastBufferWriter w) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) m.SendNamedMessage(full, clientIds, w, (NetworkDelivery)(large ? 4 : 3)); }); } } private static void Send(string name, Writer write, Action<CustomMessagingManager, string, FastBufferWriter> send) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter writer = default(FastBufferWriter); ((FastBufferWriter)(ref writer))..ctor(256, (Allocator)2, 65536); try { write(ref writer); send(Messaging, "BestiaryRedux." + name, writer); } catch (Exception arg) { Log.Error(string.Format("Network: could not send {0}: {1}", "BestiaryRedux." + name, arg)); } finally { ((FastBufferWriter)(ref writer)).Dispose(); } } } internal class NetworkModule : BestiaryRedux.Core.Module { private const string HelloMessage = "Hello"; private const string HostConfigMessage = "HostConfig"; private const float ReplyWait = 15f; private static readonly string Version = "1.0.0"; private static readonly Dictionary<ulong, string> modded = new Dictionary<ulong, string>(); private static readonly Dictionary<ulong, string> problems = new Dictionary<ulong, string>(); private static string? hostVersion; private static int session; private static string? pausedReason; private static NetworkModule? instance; internal static IEnumerable<ulong> ModdedClients => modded.Keys; internal static int Round { get; private set; } internal static string? ClientPause => pausedReason; public override string Name => "Network"; public override bool ShouldRun => true; internal static event Action<ulong>? PlayerReady; internal NetworkModule() { instance = this; } protected override void RegisterHooks() { NetChannel.On("Hello", OnHello); NetChannel.On("HostConfig", OnHostConfig); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.Start += new hook_Start(StartOfRound_Start); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.Start -= new hook_Start(StartOfRound_Start); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.StartGame += new hook_StartGame(StartOfRound_StartGame); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.StartGame -= new hook_StartGame(StartOfRound_StartGame); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnClientConnect += new hook_OnClientConnect(StartOfRound_OnClientConnect); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnClientConnect -= new hook_OnClientConnect(StartOfRound_OnClientConnect); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnClientDisconnect += new hook_OnClientDisconnect(StartOfRound_OnClientDisconnect); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnClientDisconnect -= new hook_OnClientDisconnect(StartOfRound_OnClientDisconnect); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnDestroy += new hook_OnDestroy(StartOfRound_OnDestroy); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnDestroy -= new hook_OnDestroy(StartOfRound_OnDestroy); }); } private static void Reset() { session++; Round++; modded.Clear(); problems.Clear(); pausedReason = null; hostVersion = null; SharedSettings.Clear(); } private void StartOfRound_Start(orig_Start orig, StartOfRound self) { orig.Invoke(self); Run(delegate { Reset(); NetChannel.RegisterAll(); if (NetChannel.IsHost) { modded[NetChannel.LocalClientId] = Version; Log.Debug("Network: lobby started as host (v" + Version + ")"); LogPlayers(); } else { Log.Debug("Network: joined a lobby, sending handshake to the host (v" + Version + ")"); NetChannel.SendToHost("Hello", delegate(ref FastBufferWriter w) { ((FastBufferWriter)(ref w)).WriteValueSafe(Version, false); }); int lobby = session; Runner.After(15f, delegate { Run(delegate { if (lobby == session && hostVersion == null && NetChannel.InSession) { Log.Warning("Network: the host doesn't have BestiaryRedux (no reply). Its changes won't happen in this lobby."); Tip("The host doesn't have BestiaryRedux", "Its changes won't happen in this lobby."); } }); }); } }); } private void StartOfRound_StartGame(orig_StartGame orig, StartOfRound self) { orig.Invoke(self); Run(delegate { if (NetChannel.IsHost) { Round++; pausedReason = CurrentProblem(); if (pausedReason != null) { Log.Debug("Network: new round; modules that need every player stay paused: " + pausedReason); } } }); } private void StartOfRound_OnClientConnect(orig_OnClientConnect orig, StartOfRound self, ulong clientId) { orig.Invoke(self, clientId); Run(delegate { if (NetChannel.IsHost && clientId != NetChannel.LocalClientId) { int lobby = session; Runner.After(15f, delegate { Run(delegate { if (lobby == session && !modded.ContainsKey(clientId) && NetChannel.IsHost && NetworkManager.Singleton.ConnectedClientsIds.Contains(clientId)) { Flag(clientId, "doesn't have BestiaryRedux"); } }); }); } }); } private void StartOfRound_OnClientDisconnect(orig_OnClientDisconnect orig, StartOfRound self, ulong clientId) { string name = Run(() => PlayerName(clientId), $"client {clientId}"); orig.Invoke(self, clientId); Run(delegate { if (NetChannel.IsHost && (modded.Remove(clientId) | problems.Remove(clientId))) { Log.Debug("Network: " + name + " left"); LogPlayers(); } }); } private void StartOfRound_OnDestroy(orig_OnDestroy orig, StartOfRound self) { Run(delegate { Reset(); Runner.EndOfFrame("Module refresh", ModuleRegistry.Refresh); }); orig.Invoke(self); } private void OnHello(ulong sender, FastBufferReader reader) { //IL_0007: 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) if (!NetChannel.IsHost) { return; } Run(delegate { string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); modded[sender] = text; Log.Debug("Network: handshake from " + PlayerName(sender) + " (v" + text + ")"); problems.Remove(sender); SendHostConfig(new ulong[1] { sender }); NetworkModule.PlayerReady?.Invoke(sender); if (text != Version) { Flag(sender, "has BestiaryRedux v" + text + " instead of v" + Version); } LogPlayers(); }); } private void OnHostConfig(ulong sender, FastBufferReader reader) { //IL_0007: 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) if (NetChannel.IsHost || sender != NetChannel.HostClientId) { return; } Run(delegate { //IL_001e: Unknown result type (might be due to invalid IL or missing references) string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); bool flag = hostVersion == null; hostVersion = text; string text2 = SharedSettings.Read(reader); if (flag && text != Version) { Log.Warning("Network: the host has BestiaryRedux v" + text + ", you have v" + Version + ". Everyone needs the same version; some of its changes are paused."); Tip("BestiaryRedux version mismatch", "Host has v" + text + ", you have v" + Version + ". Use the same version."); } Log.Debug("Network: settings received from the host (v" + text + "): " + text2); Runner.EndOfFrame("Module refresh", ModuleRegistry.Refresh); }); } private static void Flag(ulong clientId, string problem) { string text = PlayerName(clientId); string text2 = ChatName(clientId); problems[clientId] = text + " " + problem; if (pausedReason == null) { pausedReason = problems[clientId]; } Log.Warning("Network: " + text + " " + problem + ". Every player needs BestiaryRedux v" + Version + "; changes that need every player are paused this round."); Tip(PlainName(clientId) + " " + problem, "Everyone needs v" + Version + ". Some changes are paused."); Chat("BestiaryRedux: " + text2 + " " + problem + ". Everyone needs v" + Version + "."); LogPlayers(); } private static string? CurrentProblem() { HashSet<ulong> connected = new HashSet<ulong>(NetworkManager.Singleton.ConnectedClientsIds); return (from kv in problems where connected.Contains(kv.Key) select kv.Value).FirstOrDefault(); } private static void SendHostConfig(IReadOnlyList<ulong> clients) { NetChannel.SendTo("HostConfig", clients, delegate(ref FastBufferWriter w) { ((FastBufferWriter)(ref w)).WriteValueSafe(Version, false); SharedSettings.Write(ref w); }); Log.Debug("Network: settings sent to " + string.Join(", ", clients.Select(PlayerName)) + ": " + SharedSettings.Snapshot()); } internal static void OnConfigChanged() { instance?.Run(delegate { if (NetChannel.IsHost && SharedSettings.TakeChange()) { List<ulong> list = modded.Keys.Where((ulong id) => id != NetChannel.LocalClientId).ToList(); if (list.Count == 0) { Log.Debug("Network: host settings changed (" + SharedSettings.Snapshot() + "); no other players with the mod to send them to"); } else { SendHostConfig(list); } } }); } private static void Tip(string header, string body) { if (!((Object)(object)HUDManager.Instance == (Object)null)) { HUDManager.Instance.DisplayTip(header, body, true, false, "LC_Tip1"); Log.Debug("Network: tip shown: " + header + " / " + body); } } private static void Chat(string text) { try { HUDManager obj = HUDManager.Instance; if (obj != null) { obj.AddTextToChatOnServer(text, -1); } } catch (Exception ex) { Log.Warning("Network: chat line not sent: " + ex.Message); } } private static PlayerControllerB? Player(ulong clientId) { return StartOfRound.Instance?.allPlayerScripts?.FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => (Object)(object)x != (Object)null && x.actualClientId == clientId && (x.isPlayerControlled || x.isPlayerDead))); } private static string PlayerName(ulong clientId) { PlayerControllerB val = Player(clientId); if (!((Object)(object)val != (Object)null)) { return $"client {clientId}"; } return $"{val.playerUsername} (client {clientId})"; } private static string PlainName(ulong clientId) { return Player(clientId)?.playerUsername ?? "A player"; } private static string ChatName(ulong clientId) { PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts; int num = ((array == null) ? (-1) : Array.FindIndex(array, (PlayerControllerB x) => (Object)(object)x != (Object)null && x.actualClientId == clientId && (x.isPlayerControlled || x.isPlayerDead))); if (num < 0) { return "A player"; } return $"[playerNum{num}]"; } private static void LogPlayers() { if (ModConfig.DebugLogging && NetChannel.IsHost) { string value; IEnumerable<string> values = NetworkManager.Singleton.ConnectedClientsIds.Select((ulong id) => PlayerName(id) + ((id == NetChannel.LocalClientId) ? ", host" : "") + (modded.TryGetValue(id, out value) ? (", v" + value) : ", no mod or no reply yet")); Log.Debug("Network: players: " + string.Join("; ", values) + ((pausedReason != null) ? " (modules that need every player paused this round)" : "")); } } } internal static class SharedSettings { private static readonly List<ConfigLayout.Setting> synced = new List<ConfigLayout.Setting>(); private static readonly Dictionary<string, object> fromHost = new Dictionary<string, object>(); private static string lastSeen = ""; private static string Id(ConfigLayout.Setting s) { return s.Definition.Section + "/" + s.Definition.Key; } internal static void Add(ConfigLayout.Setting s) { synced.Add(s); } internal static T Get<T>(ConfigLayout.Setting<T> s) { if (!NetChannel.IsHost && fromHost.TryGetValue(Id(s), out object value) && value is T) { return (T)value; } return s.Value; } internal static void Clear() { fromHost.Clear(); lastSeen = Snapshot(); } internal static bool TakeChange() { string text = Snapshot(); if (text == lastSeen) { return false; } lastSeen = text; return true; } internal static string Snapshot() { return string.Join(", ", synced.Select((ConfigLayout.Setting s) => s.Definition.Key + "=" + s.Serialized)); } internal static void Write(ref FastBufferWriter w) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) int count = synced.Count; ((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref count, default(ForPrimitives)); foreach (ConfigLayout.Setting item in synced) { ((FastBufferWriter)(ref w)).WriteValueSafe(Id(item), false); ((FastBufferWriter)(ref w)).WriteValueSafe(item.Serialized, false); } } internal static string Read(FastBufferReader r) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) fromHost.Clear(); int num = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives)); List<string> list = new List<string>(); string id = default(string); string text = default(string); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref r)).ReadValueSafe(ref id, false); ((FastBufferReader)(ref r)).ReadValueSafe(ref text, false); list.Add(id.Substring(id.IndexOf('/') + 1) + "=" + text); ConfigLayout.Setting setting = synced.FirstOrDefault((ConfigLayout.Setting x) => Id(x) == id); if (setting != null) { try { fromHost[id] = TomlTypeConverter.ConvertToValue(text, setting.SettingType); } catch { Log.Warning("Network: host value \"" + text + "\" for " + id + " could not be read; using your own."); } } } return string.Join(", ", list); } } } namespace BestiaryRedux.ItemUse { internal static class ActiveUseTracker { private class ActiveUse { public EnemyAI? By; public string Module = ""; public string What = ""; public Action TurnOff = delegate { }; } private static readonly Dictionary<GrabbableObject, ActiveUse> active = new Dictionary<GrabbableObject, ActiveUse>(); internal static void Started(GrabbableObject item, EnemyAI by, string module, string what, Action turnOff) { active[item] = new ActiveUse { By = by, Module = module, What = what, TurnOff = turnOff }; Log.Debug("Item use: " + Name(item) + " " + what + " (by " + (by?.enemyType?.enemyName ?? "an enemy") + ")"); } internal static bool StartedByUs(GrabbableObject item) { if ((Object)(object)item != (Object)null) { return active.ContainsKey(item); } return false; } internal static void Stop(GrabbableObject item, string reason) { if (!((Object)(object)item == (Object)null) && active.TryGetValue(item, out ActiveUse value)) { active.Remove(item); ItemErrorGuard.Run(item, "turning off", value.TurnOff, checkBlock: false); Log.Debug("Item use: " + Name(item) + " switched off (" + reason + ")"); } } internal static void StopAllBy(EnemyAI enemy, string reason) { StopWhere((ActiveUse u) => u.By == enemy, reason); } internal static void StopAllFrom(string module, string reason) { StopWhere((ActiveUse u) => u.Module == module, reason); } internal static void StopAll(string reason) { StopWhere((ActiveUse _) => true, reason); } internal static void Forget(GrabbableObject item) { active.Remove(item); } internal static void Clear() { active.Clear(); } private static void StopWhere(Func<ActiveUse, bool> match, string reason) { foreach (GrabbableObject item in active.Keys.Where((GrabbableObject k) => (Object)(object)k == (Object)null).ToList()) { active.Remove(item); } foreach (GrabbableObject item2 in (from kv in active where match(kv.Value) select kv.Key).ToList()) { Stop(item2, reason); } } private static string Name(GrabbableObject item) { return item.itemProperties?.itemName ?? ((Object)item).name; } } internal static class CodeScan { private const int MaxDepth = 3; private static readonly Dictionary<short, OpCode> opcodes = (from f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public) select f.GetValue(null)).OfType<OpCode>().ToDictionary((OpCode o) => o.Value); internal static string? NeedsPlayer(MethodInfo method, Type itemType) { return Scan(method, itemType, 0, new HashSet<MethodBase>()); } private static string? Scan(MethodBase m, Type itemType, int depth, HashSet<MethodBase> visited) { if (!visited.Add(m)) { return null; } byte[] array; try { array = m.GetMethodBody()?.GetILAsByteArray(); } catch { return null; } if (array == null) { return null; } Type[] typeArgs = ((m.DeclaringType != null && m.DeclaringType.IsGenericType) ? m.DeclaringType.GetGenericArguments() : Type.EmptyTypes); Type[] methodArgs = (m.IsGenericMethod ? m.GetGenericArguments() : Type.EmptyTypes); int num2; for (int i = 0; i < array.Length; i += num2) { short num = array[i++]; if (num == 254 && i < array.Length) { num = (short)(0xFE00 | array[i++]); } if (!opcodes.TryGetValue(num, out var value)) { return null; } num2 = OperandSize(value.OperandType, array, i); if (value.OperandType != OperandType.InlineField && value.OperandType != OperandType.InlineMethod && value.OperandType != OperandType.InlineTok) { continue; } MemberInfo memberInfo = Resolve(m.Module, BitConverter.ToInt32(array, i), typeArgs, methodArgs); string text = NeedsPlayer(memberInfo); if (text != null) { return text + " in " + m.DeclaringType?.Name + "." + m.Name; } if (depth >= 3 || !(memberInfo is MethodBase callee)) { continue; } foreach (MethodBase item in Followed(callee, itemType)) { string text2 = Scan(item, itemType, depth + 1, visited); if (text2 != null) { return text2; } } } return null; } private static string? NeedsPlayer(MemberInfo? member) { if (member is FieldInfo fieldInfo) { if (fieldInfo.Name == "playerHeldBy" && typeof(GrabbableObject).IsAssignableFrom(fieldInfo.DeclaringType)) { return "playerHeldBy"; } if (fieldInfo.DeclaringType == typeof(GameNetworkManager) && fieldInfo.Name == "localPlayerController") { return "the local player"; } if (fieldInfo.DeclaringType == typeof(PlayerControllerB)) { return "player." + fieldInfo.Name; } } else if (member is MethodBase methodBase && methodBase.DeclaringType == typeof(PlayerControllerB)) { return "player." + methodBase.Name; } return null; } private static IEnumerable<MethodBase> Followed(MethodBase callee, Type itemType) { Type t = callee.DeclaringType; if (t == null) { yield break; } if (IsItemClass(t, itemType)) { if (callee is MethodInfo { IsVirtual: not false } methodInfo && t != itemType) { MethodInfo method = itemType.GetMethod(methodInfo.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, (from p in methodInfo.GetParameters() select p.ParameterType).ToArray(), null); if (method != null) { yield return method; yield break; } } yield return callee; } else { if (!IsNestedInItemClass(t, itemType)) { yield break; } yield return callee; if (callee is ConstructorInfo) { MethodInfo method2 = t.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { yield return method2; } } } } private static bool IsItemClass(Type t, Type itemType) { if (t != typeof(GrabbableObject) && typeof(GrabbableObject).IsAssignableFrom(t)) { return t.IsAssignableFrom(itemType); } return false; } private static bool IsNestedInItemClass(Type t, Type itemType) { Type declaringType = t.DeclaringType; while (declaringType != null) { if (IsItemClass(declaringType, itemType)) { return true; } declaringType = declaringType.DeclaringType; } return false; } private static MemberInfo? Resolve(System.Reflection.Module module, int token, Type[] typeArgs, Type[] methodArgs) { try { return module.ResolveMember(token, typeArgs, methodArgs); } catch { return null; } } private static int OperandSize(OperandType type, byte[] il, int at) { switch (type) { case OperandType.InlineNone: return 0; case OperandType.ShortInlineBrTarget: case OperandType.ShortInlineI: case OperandType.ShortInlineVar: return 1; case OperandType.InlineVar: return 2; case OperandType.InlineI8: case OperandType.InlineR: return 8; case OperandType.InlineSwitch: return 4 + 4 * BitConverter.ToInt32(il, at); default: return 4; } } } public enum ItemClass { Safe, WeaponShotgun, WeaponMelee, WeaponThrowable, Workaround, Never, Risky, NoActivation } internal static class ItemCatalog { internal class Entry { public string Name = ""; public Type Type; public ItemClass Class; public string Reason = ""; public bool Modded; public ThrowableKind Throwable; public Type? Base; public bool Generic; public bool UsableByDefault { get { if (Class != ItemClass.Safe && Class != ItemClass.WeaponShotgun && Class != ItemClass.WeaponMelee && Class != ItemClass.WeaponThrowable) { return Class == ItemClass.Workaround; } return true; } } } private static readonly Assembly GameAssembly = typeof(GrabbableObject).Assembly; private static readonly Dictionary<Type, (ItemClass cls, string reason)> vanilla = new Dictionary<Type, (ItemClass, string)> { [typeof(ShotgunItem)] = (ItemClass.WeaponShotgun, "fired with the enemy's aim, never its own use"), [typeof(Shovel)] = (ItemClass.WeaponMelee, "own hit in front of the enemy, never its own use"), [typeof(KnifeItem)] = (ItemClass.WeaponMelee, "own hit in front of the enemy, never its own use"), [typeof(FlashlightItem)] = (ItemClass.Workaround, "toggled; bulb shown on other players' screens"), [typeof(NoisemakerProp)] = (ItemClass.Safe, "plays its sound"), [typeof(BoomboxItem)] = (ItemClass.Safe, "music on/off, stopped on drop"), [typeof(RadarBoosterItem)] = (ItemClass.Safe, "on/off, stopped on drop"), [typeof(RemoteProp)] = (ItemClass.Safe, "toggles the ship lights"), [typeof(JetpackItem)] = (ItemClass.Never, "needs a player"), [typeof(PatcherTool)] = (ItemClass.Never, "needs a player"), [typeof(SprayPaintItem)] = (ItemClass.Never, "needs a player"), [typeof(KeyItem)] = (ItemClass.Never, "needs a player"), [typeof(LockPicker)] = (ItemClass.Never, "needs a player"), [typeof(ExtensionLadderItem)] = (ItemClass.Never, "needs a player"), [typeof(TetraChemicalItem)] = (ItemClass.Never, "needs a player"), [typeof(HauntedMaskItem)] = (ItemClass.Never, "needs a player"), [typeof(GiftBoxItem)] = (ItemClass.Never, "needs a player"), [typeof(BeltBagItem)] = (ItemClass.Never, "opens the bag for the local player"), [typeof(WalkieTalkie)] = (ItemClass.Never, "needs a player"), [typeof(CaveDwellerPhysicsProp)] = (ItemClass.Never, "needs a player"), [typeof(MapDevice)] = (ItemClass.Never, "needs a player") }; private static readonly Dictionary<(Item, Type), Entry> entries = new Dictionary<(Item, Type), Entry>(); internal static string Display(ItemClass c) { return c switch { ItemClass.WeaponShotgun => "Weapon (shotgun)", ItemClass.WeaponMelee => "Weapon (melee)", ItemClass.WeaponThrowable => "Weapon (throwable)", ItemClass.NoActivation => "No activation", _ => c.ToString(), }; } internal static void Clear() { entries.Clear(); } internal static Entry Get(GrabbableObject item) { return Get(item.itemProperties, item); } internal static Entry Get(Item item, GrabbableObject sample) { Type type = ((object)sample).GetType(); if (entries.TryGetValue((item, type), out Entry value)) { return value; } value = Classify(item, type, sample); entries[(item, type)] = value; return value; } internal static void DumpToLog() { if (!ModConfig.DebugLogging) { return; } List<Item> list = StartOfRound.Instance?.allItemsList?.itemsList; if (list == null) { return; } int num = 0; foreach (Item item in from x in list where (Object)(object)x != (Object)null orderby x.itemName select x) { GrabbableObject val = (((Object)(object)item.spawnPrefab != (Object)null) ? item.spawnPrefab.GetComponent<GrabbableObject>() : null); if ((Object)(object)val == (Object)null) { Log.Debug("Item catalog: \"" + item.itemName + "\" - No activation: no item script"); continue; } Entry entry = Get(item, val); string text = (entry.Modded ? (entry.Type.Name + ", " + entry.Type.Assembly.GetName().Name) : entry.Type.Name); string text2 = (ItemErrorGuard.IsBlocked(entry.Name) ? " [auto-blocked]" : ""); Log.Debug("Item catalog: \"" + entry.Name + "\" (" + text + ") - " + Display(entry.Class) + ": " + entry.Reason + text2); num++; } Log.Debug($"Item catalog: {num} items"); } private static Entry Classify(Item item, Type type, GrabbableObject sample) { Entry entry = new Entry { Name = item.itemName, Type = type, Modded = (type.Assembly != GameAssembly) }; try { StunGrenadeItem val = (StunGrenadeItem)(object)((sample is StunGrenadeItem) ? sample : null); if (val != null) { ClassifyThrowable(entry, val); } else { Entry entry2 = entry; Entry entry3 = entry; if (entry.Modded) { (entry2.Class, entry3.Reason) = ClassifyModded(entry, type); } else { (entry2.Class, entry3.Reason) = ClassifyVanilla(type); } } } catch (Exception ex) { Entry entry3 = entry; string reason = "could not be checked (" + ex.GetType().Name + ")"; entry.Class = ItemClass.Risky; entry3.Reason = reason; } return entry; } private static void ClassifyThrowable(Entry e, StunGrenadeItem g) { ThrowableKind throwableKind = (e.Throwable = Throwables.Kind(g)); e.Class = ((throwableKind == ThrowableKind.Unclear) ? ItemClass.Never : ItemClass.WeaponThrowable); string text = Throwables.Describe(g) + ((throwableKind == ThrowableKind.Unclear) ? "; not used (unknown variant)" : ""); if (!e.Modded) { e.Reason = text; return; } string[] array = ItemClassRules.ChangedBelow(e.Type, typeof(StunGrenadeItem), Throwables.BehaviourMethods); e.Base = typeof(StunGrenadeItem); e.Reason = ((throwableKind == ThrowableKind.Unclear) ? ("based on StunGrenadeItem: " + text) : ("based on StunGrenadeItem, behaves like the " + Throwables.LikeName(throwableKind) + ": " + text + ((array.Length != 0) ? ("; its own " + string.Join(", ", array) + " (watched by the error guard)") : ""))); } private static (ItemClass, string) ClassifyVanilla(Type type) { Type type2 = ItemClassRules.NearestKnown(type, typeof(GrabbableObject), vanilla.ContainsKey); if (type2 != null) { return vanilla[type2]; } if (!(ItemClassRules.ActivationFrom(type, typeof(GrabbableObject)) == null)) { return (ItemClass.Never, "vanilla item not checked for enemy use"); } return (ItemClass.NoActivation, "nothing happens when used"); } private static (ItemClass, string) ClassifyModded(Entry e, Type type) { ItemClassRules.Result result = ItemClassRules.Classify(type, typeof(GrabbableObject), vanilla.ContainsKey, (Type t) => t.Assembly == GameAssembly); switch (result.Route) { case ItemClassRules.Route.LikeVanilla: { e.Base = result.Base; (ItemClass cls, string reason) tuple2 = vanilla[result.Base]; ItemClass item2 = tuple2.cls; string item3 = tuple2.reason; bool flag = ItemClassRules.ChangedBelow(type, result.Base, new string[1] { "ItemActivate" }).Length != 0; return (item2, "based on " + result.Base.Name + ": " + item3 + (flag ? "; its own ItemActivate" : "")); } case ItemClassRules.Route.VanillaRule: e.Base = result.Base; var (item, text2) = ClassifyVanilla(result.Base); return (item, "based on " + result.Base.Name + ": " + text2); case ItemClassRules.Route.Nothing: return (ItemClass.NoActivation, "nothing happens when used"); default: { MethodInfo method = type.GetMethod("ItemActivate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(bool), typeof(bool) }, null); string text = CodeScan.NeedsPlayer(method, type); if (text != null) { return (ItemClass.Risky, "code check: uses " + text); } e.Generic = true; return (ItemClass.Safe, "its own activation, code check found nothing that needs a player; generic use"); } } } } internal static class ItemErrorGuard { private class Watch { public GrabbableObject? Item; public string ItemName = ""; public string[] TypeNames = Array.Empty<string>(); public float Until; } private sealed class ConfigList : IBlockList { public bool IsBlocked(string itemName) { return ItemErrorGuard.IsBlocked(itemName); } public void Add(string itemName) { AddBlocked(itemName); } } [CompilerGenerated] private static class <>O { public static LogCallback <0>__OnUnityLog; } private const float WatchSeconds = 5f; private static readonly List<Watch> watches = new List<Watch>(); private static HashSet<string> blocked = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static string blockedFrom = ""; private static bool listening; private static bool blocking; private static readonly ConfigList list = new ConfigList(); internal static void Init() { //IL_0018: 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_0023: Expected O, but got Unknown if (!listening) { object obj = <>O.<0>__OnUnityLog; if (obj == null) { LogCallback val = OnUnityLog; <>O.<0>__OnUnityLog = val; obj = (object)val; } Application.logMessageReceived += (LogCallback)obj; listening = true; } } internal static bool IsBlocked(string itemName) { string autoBlockedItems = ModConfig.AutoBlockedItems; if (autoBlockedItems != blockedFrom) { blockedFrom = autoBlockedItems; blocked = new HashSet<string>(Split(autoBlockedItems), StringComparer.OrdinalIgnoreCase); } return blocked.Contains(itemName.Trim()); } internal static bool Run(GrabbableObject item, string what, Action use, bool checkBlock = true) { string itemName = item.itemProperties?.itemName ?? ((Object)item).name; bool flag = ItemUseGuard.Run(list, itemName, checkBlock, use, delegate(string n, Exception e) { Blocked(item, n, what + ": " + e.GetType().Name + ": " + e.Message, e); }, delegate(string n, Exception e) { Log.Debug("Item use: \"" + n + "\" (blocked) failed again (" + what + ": " + e.GetType().Name + ": " + e.Message + ")"); }); if (flag) { watches.RemoveAll((Watch w) => w.Until < Time.realtimeSinceStartup); string[] typeNames = (from t in ItemClassRules.Line(((object)item).GetType(), typeof(GrabbableObject)) select t.FullName ?? t.Name).ToArray(); watches.Add(new Watch { Item = item, ItemName = itemName, TypeNames = typeNames, Until = Time.realtimeSinceStartup + 5f }); } return flag; } private static void OnUnityLog(string message, string stackTrace, LogType type) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (blocking || watches.Count == 0 || ((int)type != 4 && (int)type != 0)) { return; } float now = Time.realtimeSinceStartup; watches.RemoveAll((Watch w) => w.Until < now); string frame = ThrowingFrame(stackTrace); if (frame == null) { return; } Watch hit = watches.FirstOrDefault((Watch w) => w.TypeNames.Any((string t) => StartsWithType(frame, t))); if (hit != null) { watches.RemoveAll((Watch w) => w.ItemName == hit.ItemName); if (!IsBlocked(hit.ItemName)) { AddBlocked(hit.ItemName); Blocked(hit.Item, hit.ItemName, "error shortly after use: " + message, null); } } } internal static string? ThrowingFrame(string stackTrace) { string[] array = stackTrace.Split('\n'); foreach (string text in array) { string text2 = text.Trim(); if (text2.StartsWith("at ")) { text2 = text2.Substring(3); } if (text2.StartsWith("(wrapper dynamic-method) ")) { text2 = text2.Substring("(wrapper dynamic-method) ".Length); } if (text2.Length != 0 && !text2.StartsWith("UnityEngine.") && !text2.StartsWith("Unity.") && !text2.StartsWith("System.")) { return text2; } } return null; } internal static bool StartsWithType(string frame, string typeName) { if (!frame.StartsWith(typeName + ".") && !frame.StartsWith(typeName + ":")) { return frame.StartsWith(typeName + "+"); } return true; } private static void AddBlocked(string itemName) { blocking = true; try { List<string> list = Split(ModConfig.AutoBlockedItems).ToList(); list.Add(itemName.Trim()); ModConfig.SetAutoBlockedItems(string.Join(", ", list)); } finally { blocking = false; } } private static void Blocked(GrabbableObject? item, string itemName, string why, Exception? e) { Log.Warning("Item use: \"" + itemName + "\"" + (((Object)(object)item != (Object)null) ? (" (" + ((object)item).GetType().Name + ")") : "") + " is now auto-blocked for enemies after an error (" + why + "). Clear it from \"Auto-blocked items\" in 1. General to try it again."); if (e != null) { Log.Debug($"Item use: {itemName} error: {e}"); } if ((Object)(object)item != (Object)null && NetChannel.IsHost) { blocking = true; try { ActiveUseTracker.Stop(item, "auto-blocked"); } finally { blocking = false; } } } private static IEnumerable<string> Split(string list) { return from s in list.Split(',') select s.Trim() into s where s.Length > 0 select s; } } internal static class ItemUseMessage { internal delegate void ActionHandler(EnemyAI? enemy, GrabbableObject item, int value); private const string MessageName = "ItemUse"; private static readonly Dictionary<string, ActionHandler> actions = new Dictionary<string, ActionHandler>(); internal static void Init() { NetChannel.On("ItemUse", OnMessage); } internal static void On(string action, ActionHandler handler) { actions[action] = handler; } internal static bool Send(EnemyAI? enemy, GrabbableObject item, string action, int value = 0, bool turnOff = false) { if (!NetChannel.IsHost || (Object)(object)item == (Object)null) { return false; } if ((Object)(object)enemy == (Object)null) { enemy = null; } if (!RunLocal(enemy, item, action, value, !turnOff)) { Log.Debug("Item use: \"" + action + "\" didn't run on the host, not sent"); return false; } List<ulong> clientIds = NetworkModule.ModdedClients.Where((ulong id) => id != NetChannel.LocalClientId).ToList(); ulong enemyId = (((Object)(object)enemy != (Object)null) ? ((NetworkBehaviour)enemy).NetworkObjectId : 0); ulong itemId = ((NetworkBehaviour)item).NetworkObjectId; NetChannel.SendTo("ItemUse", clientIds, delegate(ref FastBufferWriter w) { //IL_0009: 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) //IL_001e: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) ((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref enemyId, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref itemId, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(action, false); ((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref value, default(ForPrimitives)); }); return true; } private static void OnMessage(ulong sender, FastBufferReader reader) { //IL_0016: 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_0029: 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_0046: 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) if (!NetChannel.IsHost && sender == NetChannel.HostClientId) { ulong num = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives)); ulong num2 = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives)); string text = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); int value = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref value, default(ForPrimitives)); EnemyAI enemy = ((num != 0L) ? Find<EnemyAI>(num) : null); GrabbableObject val = Find<GrabbableObject>(num2); if ((Object)(object)val == (Object)null) { Log.Debug($"Item use: \"{text}\" received for an item that's gone here (item {num2})"); } else { RunLocal(enemy, val, text, value, checkBlock: false); } } } private static bool RunLocal(EnemyAI? enemy, GrabbableObject item, string action, int value, bool checkBlock) { if (!actions.TryGetValue(action, out ActionHandler handler)) { Log.Debug("Item use: no handler for \"" + action + "\" on this machine"); return false; } return ItemErrorGuard.Run(item, action, delegate { handler(enemy, item, value); }, checkBlock); } private static T? Find<T>(ulong networkObjectId) where T : class { NetworkManager singleton = NetworkManager.Singleton; Dictionary<ulong, NetworkObject> dictionary = ((singleton == null) ? null : singleton.SpawnManager?.SpawnedObjects); if (dictionary == null || !dictionary.TryGetValue(networkObjectId, out var value) || (Object)(object)value == (Object)null) { return null; } return ((Component)value).GetComponent<T>(); } } internal class ItemUseModule : BestiaryRedux.Core.Module { private const float DumpDelay = 2f; public override string Name => "ItemUse"; public override bool ShouldRun => true; protected override void RegisterHooks() { ItemErrorGuard.Init(); ItemUseMessage.Init(); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.Start += new hook_Start(StartOfRound_Start); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.Start -= new hook_Start(StartOfRound_Start); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnDestroy += new hook_OnDestroy(StartOfRound_OnDestroy); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.OnDestroy -= new hook_OnDestroy(StartOfRound_OnDestroy); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.ShipLeave += new hook_ShipLeave(StartOfRound_ShipLeave); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown StartOfRound.ShipLeave -= new hook_ShipLeave(StartOfRound_ShipLeave); }); } private void StartOfRound_ShipLeave(orig_ShipLeave orig, StartOfRound self) { orig.Invoke(self); Run(delegate { if (NetChannel.IsHost) { ActiveUseTracker.StopAll("the ship is leaving"); } }); } private void StartOfRound_Start(orig_Start orig, StartOfRound self) { orig.Invoke(self); Run(delegate { ItemCatalog.Clear(); Runner.After(2f, delegate { Run(ItemCatalog.DumpToLog); }); }); } private void StartOfRound_OnDestroy(orig_OnDestroy orig, StartOfRound self) { Run(delegate { ActiveUseTracker.Clear(); ItemCatalog.Clear(); }); orig.Invoke(self); } internal static bool CanEverUse(GrabbableObject item, out string reason) { reason = ""; if ((Object)(object)item == (Object)null || (Object)(object)item.itemProperties == (Object)null) { reason = "no item"; return false; } ItemCatalog.Entry entry = ItemCatalog.Get(item); if (ItemErrorGuard.IsBlocked(entry.Name)) { reason = "auto-blocked"; return false; } if (!entry.UsableByDefault) { reason = ItemCatalog.Display(entry.Class); return false; } return true; } internal static bool BatteryEmpty(GrabbableObject item) { if ((Object)(object)item.itemProperties != (Object)null && item.itemProperties.requiresBattery) { if (item.insertedBattery != null) { return item.insertedBattery.empty; } return true; } return false; } internal static bool CanUse(GrabbableObject item, out string reason) { if (!CanEverUse(item, out reason)) { return false; } if (item.isHeld || item.isPocketed) { reason = "held by a player"; return false; } if (item.deactivated) { reason = "deactivated"; return false; } BugNest? current = BugNest.Current; if (current != null && current.IsStored(item)) { reason = "stored in the nest"; return false; } if (item.isBeingUsed && !ActiveUseTracker.StartedByUs(item)) { reason = "already in use"; return false; } return true; } } public enum ThrowableKind { None, PinGrenade, ShortFuseGrenade, LandingExplosive, Unclear } internal static class Throwables { internal const float ShortFuse = 1f; internal static readonly string[] BehaviourMethods = new string[6] { "ItemActivate", "Update", "Start", "EquipItem", "OnHitGround", "FallWithCurve" }; internal static ThrowableKind Kind(StunGrenadeItem g) { if (g.explodeOnCollision && g.dontRequirePullingPin) { return ThrowableKind.LandingExplosive; } if (!g.explodeOnCollision && !g.dontRequirePullingPin) { if (!(g.TimeToExplode < 1f)) { return ThrowableKind.PinGrenade; } return ThrowableKind.ShortFuseGrenade; } return ThrowableKind.Unclear; } internal static string LikeName(ThrowableKind kind) { return kind switch { ThrowableKind.PinGrenade => "Stun grenade", ThrowableKind.ShortFuseGrenade => "Homemade flashbang", ThrowableKind.LandingExplosive => "Easter egg", _ => "?", }; } internal static string Describe(StunGrenadeItem g) { string text = (g.spawnDamagingShockwave ? "damaging blast" : "stun flash"); switch (Kind(g)) { case ThrowableKind.PinGrenade: case ThrowableKind.ShortFuseGrenade: return string.Format("pin, fuse {0:0.##} s{1}, {2}{3}", g.TimeToExplode, (g.TimeToExplode < 1f) ? " (too short to throw)" : "", text, g.DestroyGrenade ? ", used up" : ""); case ThrowableKind.LandingExplosive: return $"goes off on landing after a throw, chance {g.chanceToExplode:0.##}%, {text}"; default: return $"unusual settings (explode on landing {g.explodeOnCollision}, no pin {g.dontRequirePullingPin})"; } } } internal class WeaponDamage : BestiaryRedux.Core.Module { internal readonly struct Rules { public readonly bool HurtPlayers; public readonly bool HurtEnemies; public readonly string Why; public bool AllowsAll { get { if (HurtPlayers) { return HurtEnemies; } return false; } } public Rules(bool hurtPlayers, bool hurtEnemies, string why) { HurtPlayers = hurtPlayers; HurtEnemies = hurtEnemies; Why = why; } } private static readonly List<Func<EnemyAI, Rules?>> providers = new List<Func<EnemyAI, Rules?>>(); private static readonly Stack<Rules> scopes = new Stack<Rules>(); private static WeaponDamage? instance; public override string Name => "WeaponDamage"; public override bool ShouldRun => true; internal static bool Ready { get { if (instance != null) { return instance.State == ModuleState.On; } return false; } } private static Rules? Current { get { if (scopes.Count <= 0) { return null; } return scopes.Peek(); } } internal WeaponDamage() { instance = this; } internal static void AddRules(Func<EnemyAI, Rules?> provider) { providers.Add(provider); } internal static Rules? RulesFor(EnemyAI? holder) { if ((Object)(object)holder == (Object)null) { return null; } foreach (Func<EnemyAI, Rules?> provider in providers) { Rules? result = provider(holder); if (result.HasValue) { return result; } } return null; } internal static void WithRules(Rules rules, Action body) { scopes.Push(rules); try { body(); } finally { scopes.Pop(); } } protected override void RegisterHooks() { Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown ShotgunItem.ShootGun += new hook_ShootGun(ShotgunItem_ShootGun); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown ShotgunItem.ShootGun -= new hook_ShootGun(ShotgunItem_ShootGun); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown PlayerControllerB.DamagePlayer += new hook_DamagePlayer(PlayerControllerB_DamagePlayer); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown PlayerControllerB.DamagePlayer -= new hook_DamagePlayer(PlayerControllerB_DamagePlayer); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown EnemyAI.HitEnemyOnLocalClient += new hook_HitEnemyOnLocalClient(EnemyAI_HitEnemyOnLocalClient); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown EnemyAI.HitEnemyOnLocalClient -= new hook_HitEnemyOnLocalClient(EnemyAI_HitEnemyOnLocalClient); }); Hook(delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown EnemyAI.SetEnemyStunned += new hook_SetEnemyStunned(EnemyAI_SetEnemyStunned); }, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown EnemyAI.SetEnemyStunned -= new hook_SetEnemyStunned(EnemyAI_SetEnemyStunned); }); } private void ShotgunItem_ShootGun(orig_ShootGun orig, ShotgunItem self, Vector3 shotgunPosition, Vector3 shotgunForward) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) Rules? rules = Run(() => RulesFor(self.heldByEnemy), null); if (!rules.HasValue || rules.Value.AllowsAll) { orig.Invoke(self, shotgunPosition, shotgunForward); return; } WithRules(rules.Value, delegate { //IL_000d: 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) orig.Invoke(self, shotgunPosition, shotgunForward); }); } private void PlayerControllerB_DamagePlayer(orig_DamagePlayer orig, PlayerControllerB self, int damageNumber, bool hasDamageSFX, bool callRPC, CauseOfDeath causeOfDeath, int deathAnimation, bool fallDamage, Vector3 force) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) Rules? rules = Current; if (rules.HasValue && !rules.Value.HurtPlayers) { Run(delegate { Log.Debug($"Weapon damage: {damageNumber} damage to {self.playerUsername} skipped ({rules.Value.Why})"); }); } else { orig.Invoke(self, damageNumber, hasDamageSFX, callRPC, causeOfDeath, deathAnimation, fallDamage, force); } } private void EnemyAI_SetEnemyStunned(orig_SetEnemyStunned orig, EnemyAI self, bool setToStunned, float setToStunTime, PlayerControllerB setStunnedByPlayer) { Rules? rules = Current; if (rules.HasValue && !rules.Value.HurtEnemies && setToStunned) { Run(delegate { Log.Debug("Weapon damage: stun on " + self.enemyType?.enemyName + " skipped (" + rules.Value.Why + ")"); }); } else { orig.Invoke(self, setToStunned, setToStunTime, setStunnedByPlayer); } } private void EnemyAI_HitEnemyOnLocalClient(orig_HitEnemyOnLocalClient orig, EnemyAI self, int force, Vector3 hitDirection, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) Rules? rules = Current; if (rules.HasValue && !rules.Value.HurtEnemies) { Run(delegate { Log.Debug("Weapon damage: hit on " + self.enemyType?.enemyName + " skipped (" + rules.Value.Why + ")"); }); } else { orig.Invoke(self, force, hitDirection, playerWhoHit, playHitSFX, hitID); } } } } namespace BestiaryRedux.Enemies { public static class CreatureBrains { public readonly struct Needs { public readonly string Feature; public readonly string[] Methods; public Needs(string feature, params string[] methods) { Feature = feature; Methods = methods; } } private static readonly Dictionary<(Type, Type, string), string?> cache = new Dictionary<(Type, Type, string), string>(); public static bool IsSubclass(Type type, Type vanilla) { if (type != vanilla) { return vanilla.IsAssignableFrom(type); } return false; } public static string? Replaced(Type type, Type vanilla, string method) { if (cache.TryGetValue((type, vanilla, method), out string value)) { return value; } string text = null; Type type2 = type; while (text == null && type2 != null && type2 != vanilla && vanilla.IsAssignableFrom(type2)) { MethodInfo method2 = type2.GetMethod(method, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (!(method2 == null) && !CallsBelow(method2, type2)) { text = type2.Name + "." + method; } type2 = type2.BaseType; } cache[(type, vanilla, method)] = text; return text; } public static List<(string Feature, string Why)> Skipped(Type type, Type vanilla, IEnumerable<Needs> features) { List<(string, string)> list = new List<(string, string)>(); if (!IsSubclass(type, vanilla)) { return list; } foreach (Needs feature in features) { string[] array = (from m in feature.Methods select Replaced(type, vanilla, m) into r where r != null select (r)).ToArray(); if (array.Length != 0) { list.Add((feature.Feature, "it replaces " + string.Join(", ", array) + " without calling the vanilla one")); } } return list; } private static bool CallsBelow(MethodInfo m, Type declaring) { byte[] array; try { array = m.GetMethodBody()?.GetILAsByteArray(); } catch (Exception) { return false; } if (array == null) { return false; } for (int i = 0; i + 4 < array.Length; i++) { if (array[i] != 40) { continue; } int metadataToken = BitConverter.ToInt32(array, i + 1); try { MethodBase methodBase = m.Module.ResolveMethod(metadataToken, declaring.IsGenericType ? declaring.GetGenericArguments() : null, null); if (methodBase != null && methodBase.Name == m.Name && methodBase.DeclaringType != null && methodBase.DeclaringType != declaring && methodBase.DeclaringType.IsAssignableFrom(declaring) && methodBase.GetParameters().Length == 0) { return true; } } catch (Exception) { } } return false; } } } namespace BestiaryRedux.Enemies.Leviathan { internal enum AttackKind { Leap, FromBelow, Charge } internal sealed class AttackPlan { public AttackKind Kind; public LeviathanPath Path; public float Offset; public Vector3 Exit; public Vector3 Target; public Vector3 Dive; public float WarnUntil; public float BreakOut; public float DiveAt; public float WarnSpeed; public float Hold; public float DiveFrom; public float ArcLength; public float Launch; public float RunUp; public string Shape = ""; public float Height; public Vector3? Peak; public readonly List<Vector3> Preview = new List<Vector3>(); internal void Rejoined() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) WarnUntil = Path.Map(WarnUntil); BreakOut = Path.Map(BreakOut); DiveAt = Path.Map(DiveAt); if (Hold > 0f) { Hold = Path.Map(Hold); } if (DiveFrom > 0f) { DiveFrom = Path.Map(DiveFrom); } Preview.Clear(); for (float num = 0f; num <= DiveAt + 20f && num <= Path.Length; num += 4f) { Preview.Add(Path.At(num).Position); } } } internal static class LeviathanAttack { private sealed class LeapArc { public List<Vector3> Points; public Vector3 Land; public Vector3 Tangent; public float A1; public float A2; public float Height; public float Length; public string Mode = ""; } private sealed class Points { public readonly List<Vector3> List = new List<Vector3>(); public float Length; public Vector3 Last => List[List.Count - 1]; public void Add(Vector3 p) { //IL_003f: 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) if (List.Count > 0) { Length += Vector3.Distance(List[List.Count - 1], p); } List.Add(p); } public Points Copy() { Points points = new Points { Length = Length }; points.List.AddRange(List); return points; } public void Curve(Vector3 b, Vector3 c, Vector3 d, int steps = 60) { //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_000c: 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_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) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 last = Last; for (int i = 1; i <= steps; i++) { Add(Bezier(last, b, c, d, (float)i / (float)steps)); } } public void Deep(Vector3 to, float depth, float ease = 0f) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) foreach (bool item in DeepSteps(to, depth, ease)) { } } public IEnumerable<bool> DeepSteps(Vector3 to, float depth, float ease = 0f, int every = 16) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Vector3 from = Last; float length = Flat(from, to); List<float> ys = new List<float>(); List<Vector3> xz = new List<Vector3>(); float lastY = from.y + depth; for (float s = 2f; s <= length; s += 2f) { Vector3 p = Vector3.Lerp(new Vector3(from.x, 0f, from.z), new Vector3(to.x, 0f, to.z), s / length); if (LeviathanPath.GroundAt(new Vector3(p.x, lastY, p.z), out var y)) { lastY = y; } if (ys.Count % every == every - 1) { yield return false; } float num = lastY - depth; if (s < ease) { num = Mathf.Lerp(from.y, num, Mathf.SmoothStep(0f, 1f, s / ease)); } ys.Add(num); xz.Add(p); } for (int i = 0; i < ys.Count; i++) { float num2 = 0f; int num3 = 0; for (int j = Mathf.Max(0, i - 3); j <= Mathf.Min(ys.Count - 1, i + 3); j++) { num2 += ys[j]; num3++; } Add(new Vector3(xz[i].x, ((float)i * 2f < ease) ? ys[i] : (num2 / (float)num3), xz[i].z)); } } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct <>c__DisplayClass41_0 { public Dictionary<string, int> misses; } private const float BelowRise = 20f; private const float HoldBack = 12f; private const float HoldOff = 10f; internal static readonly AttackPlan Fits = new AttackPlan(); private const float LaunchStep = 15f; private const float HighAbove = 8f; private const float CatchAbove = 0.5f; private static readonly Collider[] overlap = (Collider[])(object)new Collider[16]; internal const string Dips = "joined to its heading, the head would go back under the ground before the crest"; internal const string TargetOn = "the target stands on"; private const float ChargeReach = 0.6f; internal static (float min, float max) Range(AttackKind kind, float radius = 0f) { (float, float) tuple = kind switch { AttackKind.Leap => (LeviathanTuning.LeapMin.Current, LeviathanTuning.LeapMax.Current), AttackKind.FromBelow => (LeviathanTuning.FromBelowMin.Current, LeviathanTuning.FromBelowMax.Current), _ => (LeviathanTuning.ChargeMin.Current, LeviathanTuning.ChargeMax.Current), }; float item = tuple.Item1; float item2 = tuple.Item2; float num = RunUp(kind, radius); return (min: item + num, max: Mathf.Max(item + 10f, item2) + num); } internal static float Ideal(AttackKind kind, float radius = 0f) { var (num, num2) = Range(kind); return Mathf.Clamp(kind switch { AttackKind.FromBelow => LeviathanTuning.FromBelowIdeal.Current, AttackKind.Leap => LeviathanTuning.LeapIdeal.Current, _ => LeviathanTuning.ChargeIdeal.Current, }, num, num2) + RunUp(kind, radius); } internal static bool InRange(AttackKind kind, float d, float radius = 0f) { if (d >= Range(kind, radius).min) { return d <= Range(kind, radius).max; } return false; } internal static float RunUp(AttackKind kind, float radius) { if (!(radius <= 0f)) { switch (kind) { case AttackKind.FromBelow: break; case AttackKind.Leap: return LeapAhead(0.5f * radius + 25f); default: { Slide(0f, LeviathanTuning.ChargeRide.Current * radius + radius + 25f, LeviathanTuning.ChargeRiseAngle.Current, out var length); return length; } } } return 0f; } internal static float LeapAhead(float depth) { float current = LeviathanTuning.LeapExit.Current; if (depth <= 1f) { return current; } float num = Mathf.Clamp(LeviathanTuning.LeapExitAngle.Current, 30f, 90f) * (MathF.PI / 180f); float num2 = Mathf.Max(25f, 1.25f * LeviathanTuning.MinTurnAttack.Current); return Mathf.Max(current, num2 * Mathf.Sin(num) + Mathf.Max(0f, depth - num2 * (1f - Mathf.Cos(num))) / Mathf.Max(0.05f, Mathf.Tan(num))); } internal static bool AtSurface(Vector3 head, float radius, out float ground) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (LeviathanPath.GroundAt(head, out ground)) { return head.y > ground - 0.25f * radius; } return false; } internal static string Name(AttackKind kind) { if (kind != AttackKind.FromBelow) { return kind.ToString(); } return "From below"; } private static float Flat(Vector3 a, Vector3 b) { //IL_0000: 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_000d: 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_001a: 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) Vector2 val = new Vector2(a.x - b.x, a.z - b.z); return ((Vector2)(ref val)).magnitude; } private static Vector3 FlatDir(Vector3 from, Vector3 to) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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) Vector3 val = to - from; val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { return ((Vector3)(ref val)).normalized; } return Vector3.forward; } internal static AttackPlan? Plan(AttackKind kind, Vector3 start, Vector3 target, LeviathanBrain worm, Random rng, out string why, bool fitOnly = false, float? joinAt = null) { //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) PlanJob planJob = new PlanJob(kind, start, target, worm, rng, fitOnly, joinAt); planJob.RunAll(); why = planJob.Why; return planJob.Result; } internal static IEnumerable<bool> Steps(PlanJob job) { return job.Kind switch { AttackKind.Leap => Leap(job), AttackKind.FromBelow => FromBelow(job), _ => Charge(job), }; } private static string? Spot(Vector3 p, LeviathanBrain worm, string what) { //IL_0000: 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_0023: 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) if (!LeviathanPath.GroundAt(p, out var y)) { return what + " is off the map"; } if (worm.Zones.Inside(new Vector3(p.x, y, p.z), worm.Radius, out string zone)) { return what + " is in a safe zone (" + zone + ")"; } return null; } private static string? ArcFits(List<Vector3> arc, LeviathanBrain worm) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) int num = 0; for (int i = 1; i <= 9; i++) { Vector3 val = arc[arc.Count * i / 10 - 1]; if (!LeviathanPath.GroundAt(val, out var y)) { return "its arc goes off the map"; } if (val.y < y) { num++; } if (val.y - y < 2f * worm.Radius) { string text = Spot(val, worm, "its arc"); if (text != null) { return text; } } } if (num <= Mathf.RoundToInt(LeviathanTuning.ArcHump.Current)) { return null; } return $"the ground is in the way of its arc ({num} of 9 points under it)"; } private static float DiveIn(Points pts, Vector3 dir, Vector3 tangent, float a2, float after, float under, out Vector3 end) { //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_0018: Unknown result type (might be due to invalid IL or missing references) //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_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_002f: 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) //IL_003f: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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_00f9: 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_0144: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) Vector3 last = pts.Last; float num = Mathf.Max(0.26f, Mathf.Sin(a2)); Vector3 at = last + dir * 40f; float y; float num2 = Mathf.Max(10f, last.y - ((LeviathanPath.GroundAt(at, out y) ? y : last.y) - after)); float num3 = Mathf.Max(40f, 1.3f * num2 * Mathf.Cos(a2) / num + 25f); at = last + dir * num3; at.y = (LeviathanPath.GroundAt(at, out var y2) ? y2 : (last.y - num2)) - after; Vector3 b = last + tangent * (0.55f * (last.y - at.y) / num); Vector3 c = at - dir * (0.4f * num3); float num4 = -1f; for (int i = 1; i <= 40; i++) { pts.Add(Bezier(last, b, c, at, (float)i / 40f)); if (num4 < 0f && LeviathanPath.GroundAt(pts.Last, out var y3) && pts.Last.y < y3 - under) { num4 = pts.Length; } } end = at; if (!(num4 < 0f)) { return num4; } return pts.Length; } private static Vector3 Top(List<Vector3> arc) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //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_0027: 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_0042: Unknown result type (might be due to invalid IL or missing references) Vector3 val = arc[0]; foreach (Vector3 item in arc) { if (item.y > val.y) { val = item; } } return val; } private static void LogRaised(PlanJob job, AttackKind kind, float height, float length, float a1, float a2) { var (num, num2, _, _) = Shape(kind); if (a1 * 57.29578f > num + 0.5f || a2 * 57.29578f > num2 + 0.5f) { job.Note($"{job.Worm.Label}: {Name(kind)}: its arc ({height:0} m high, {length:0} m long) is too steep for the set angles; it leaves at {a1 * 57.29578f:0}° (set {num:0}°) and comes in at {a2 * 57.29578f:0}° (set {num2:0}°)"); } } private static void Recovery(List<Vector3> corners, Vector3 dive, Vector3 dir, LeviathanBrain worm) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_000c: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_001b: 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_001d: 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_0068: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0061: Unknown result type (might be due to invalid IL or missing references) Vector3 val = dive + dir * 60f; corners.Add(val); Vector3 val2 = val; Vector3 val3 = dir; if (Flat(val, worm.Territory) > 60f) { val3 = FlatDir(val, worm.Territory); ((Vector3)(ref val2))..ctor(worm.Territory.x, val.y, worm.Territory.z); corners.Add(val2); } corners.Add(val2 + val3 * (worm.Body.Length + 200f)); } private static void Finish(AttackPlan plan) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) for (float num = 0f; num <= plan.DiveAt + 20f && num <= plan.Path.Length; num += 4f) { plan.Preview.Add(plan.Path.At(num).Position); } } private static IEnumerable<bool> Leap(PlanJob job) { Vector3 start = job.Start; Vector3 target = job.Target; LeviathanBrain worm = job.Worm; float r = worm.Radius; (float min, float max) tuple = Range(AttackKind.Leap); float item = tuple.min; float item2 = tuple.max; float current = LeviathanTuning.LeapSlack.Current; Vector3 dir = FlatDir(start, target); float gH; bool fromHead = AtSurface(start, r, out gH); Vector3 at = start + dir * LeviathanTuning.LeapExit.Current; float y; float depth = (LeviathanPath.GroundAt(at, out y) ? y : start.y) - 0.5f * r - start.y; float num = (fromHead ? 0f : LeapAhead(depth)); float toTarget = Flat(start, target); float num2 = toTarget - num; if (num2 < item - current || num2 > item2 + current) { job.Why = $"the target is {num2:0} m from where it would break out ({num:0} m ahead; {item - current:0}-{item2 + current:0} m)"; yield break; } string why; if ((why = Spot(target, worm, "the target's spot")) != null) { job.Why = why; yield break; } List<string> misses = new List<string>(); List<float> list = new List<float> { num2 }; if (!fromHead) { for (float num3 = num2 - 15f; num3 >= item - current; num3 -= 15f) { list.Add(num3); } } foreach (float d1 in list) { yield return false; float ahead = toTarget - d1; Vector3 exit = start + dir * ahead; if (!LeviathanPath.GroundAt(exit, out var y2)) { misses.Add("its exit is off the map"); continue; } exit.y = y2; string text = Spot(exit, worm, "the exit point") ?? LeviathanSurfaces.AttackOut(fromHead ? start : exit, "its break-out"); if (text != null) { misses.Add($"{d1:0} m: {text}"); continue; } Vector3 p0 = (Vector3)(fromHead ? start : new Vector3(exit.x, y2 - 0.5f * r, exit.z)); Found<LeapArc> found = new Found<LeapArc>(); foreach (bool item3 in LeapArcs(p0, exit, dir, d1, target, worm, misses, found)) { yield return item3; } LeapArc value = found.Value; if (value == null) { continue; } foreach (bool item4 in LeapPlan(job, exit, p0, dir, ahead, d1, depth, fromHead, gH, value)) { yield return item4; } if (job.Result == null) { misses.Add($"{d1:0} m: {job.Why}"); continue; } yield break; } job.Why = string.Join("; ", misses.Distinct().Take(6)); job.Note(worm.Label + ": Leap: no arc fits (" + job.Why + ")"); } private static IEnumerable<bool> LeapArcs(Vector3 p0, Vector3 exit, Vector3 dir, float d1, Vector3 target, LeviathanBrain worm, List<string> misses, Found<LeapArc> into) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) float radius = worm.Radius; float catchY = target.y + 0.5f * radius; float maxHeight = LeviathanTuning.LeapMaxHeight.Current; Vector3 near = target + dir * LeviathanTuning.LeapPast.Current; float k; float[] array; if (!LeviathanPath.GroundAt(near, out var gNear) || !(target.y > Mathf.Max(exit.y, gNear) + 8f)) { float full = Mathf.Clamp(d1 * LeviathanTuning.LeapHeightPerMetre.Current, LeviathanTuning.LeapHeightMin.Current, Mathf.Max(LeviathanTuning.LeapHeightMin.Current, LeviathanTuning.LeapHeightMax.Current)); string sizes = ""; array = LeviathanTuning.Scales(LeviathanTuning.LeapFallback1, LeviathanTuning.LeapFallback2); for (int i = 0; i < array.Length; i++) { k = array[i]; yield return false; string why; LeapArc leapArc = Through(p0, dir, near, Mathf.Max((p0.y + gNear) / 2f + full * k, Mathf.Max(p0.y, gNear) + 10f), worm, null, out why); if (leapArc != null) { leapArc.Height = full * k; leapArc.Mode = ((k >= 0.999f) ? "landing on the target, the full size" : $"landing on the target, fallback {k * 100f:0} % (the full {full:0} m: {sizes})"); into.Value = leapArc; yield break; } sizes = sizes + ((sizes.Length > 0) ? "; " : "") + $"{full * k:0} m: {why}"; misses.Add(Prefix($"landing on the target {full * k:0} m high: {why}")); } } float num = catchY - p0.y; if (num > maxHeight) { misses.Add(Prefix($"no Leap arc reaches the player under Leap max height (its crest {num:0} m over the break-out, max {maxHeight:0} m)")); } else if (num >= 10f) { float num2 = 1f + Mathf.Clamp01(LeviathanTuning.LeapSymmetry.Current); float full = exit.y; Vector3 land = target; for (int j = 0; j < 4; j++) { float num3 = d1 * Mathf.Max(10f, catchY - full) * num2 / num; land = target + dir * num3; if (!LeviathanPath.GroundAt(land, out full)) { break; } } yield return false; string why2; LeapArc leapArc2 = Through(p0, dir, new Vector3(land.x, full, land.z), catchY, worm, target, out why2); if (leapArc2 != null) { leapArc2.Height = catchY - p0.y; leapArc2.Mode = $"its crest through the target ({target.y - Mathf.Max(exit.y, gNear):0} m over the ground)"; into.Value = leapArc2; yield break; } misses.Add(Prefix("its crest through the target: " + why2)); } array = new float[4] { 20f, 40f, 60f, 80f }; foreach (float full in array) { yield return false; Vector3 land = target + dir * (LeviathanTuning.LeapPast.Current + full); if (!LeviathanPath.GroundAt(land, out k)) { continue; } land.y = k; float lo = Mathf.Max(Mathf.Max(p0.y, k) + 10f, catchY); float hi = p0.y + maxHeight; if (lo > hi) { misses.Add(Prefix($"landing {full:0} m beyond: no Leap arc reaches the player under Leap max height")); continue; } string why3 = null; for (int l = 0; l < 14; l++) { if (!(hi - lo > 0.5f)) { break; } float num4 = (lo + hi) / 2f; Vector3 tangent; float a; float a2; List<Vector3> list = EruptionArc(p0, dir, num4 - p0.y, num4 - k, Flat(p0, land), 120, AttackKind.Leap, out tangent, out a, out a2); if (list == null) { lo = num4; continue; } float num5 = HeightAt(list, p0, dir, d1); if (num5 < catchY) { lo = num4; } else { hi = num4; } if (l % 4 == 3) { yield return false; } } LeapArc leapArc3 = Through(p0, dir, land, hi, worm, target, out why3); if (leapArc3 != null) { leapArc3.Height = hi - p0.y; leapArc3.Mode = $"passing the target on its way down, landing {full:0} m beyond"; into.Value = leapArc3; break; } misses.Add(Prefix($"passing the target, landing {full:0} m beyond: {why3}")); } string Prefix(string mode) { return $"{d1:0} m, {mode}"; } } private static LeapArc? Through(Vector3 p0, Vector3 dir, Vector3 land, float top, LeviathanBrain worm, Vector3? target, out string? why) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) float radius = worm.Radius; if (!LeviathanPath.GroundAt(land, out var y)) { why = "its landing is off the map"; return null; } land.y = y; if ((why = Spot(land, worm, "its landing")) != null || (why = LeviathanSurfaces.AttackOut(land, "its landing")) != null) { return null; } if (top - p0.y > LeviathanTuning.LeapMaxHeight.Current) { why = $"no Leap arc reaches the player under Leap max height ({top - p0.y:0} m, max {LeviathanTuning.LeapMaxHeight.Current:0} m)"; return null; } float length = Flat(p0, land); Vector3 tangent; float a; float a2; List<Vector3> list = EruptionArc(p0, dir, top - p0.y, top - y, length, 120, AttackKind.Leap, out tangent, out a, out a2); if (list == null) { why = "its landing is too far below the exit for the arc's angles"; return null; } if ((why = ArcFits(list, worm)) != null) { return null; } if (target.HasValue) { float num = Closest(list, target.Value) - radius; if (num > 0f) { why = $"it passes {num:0.0} m from the target"; return null; } } if ((why = ThroughStructure(list, p0, land, target, radius)) != null) { return null; } for (float num2 = 0f; num2 <= 40f; num2 += 8f) { if ((why = Spot(land + dir * num2, worm, "its dive")) != null) { return null; } } why = null; return new LeapArc { Points = list, Land = land, Tangent = tangent, A1 = a, A2 = a2, Length = length }; } private static float HeightAt(List<Vector3> arc, Vector3 p0, Vector3 dir, float x) { //IL_0006: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //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_002b: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0086: Unknown result type (might be due to invalid IL or missing references) float num = 0f; Vector3 val = p0; foreach (Vector3 item in arc) { float num2 = Vector3.Dot(new Vector3(item.x - p0.x, 0f, item.z - p0.z), dir); if (num2 >= x) { return Mathf.Lerp(val.y, item.y, Mathf.InverseLerp(num, num2, x)); } num = num2; val = item; } return val.y; } private static float Closest(List<Vector3> arc, Vector3 feet) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0043: 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_0050: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; float current = LeviathanTuning.StandingHeight.Current; Vector3 val = default(Vector3); foreach (Vector3 item in arc) { ((Vector3)(ref val))..ctor(feet.x, Mathf.Clamp(item.y, feet.y, feet.y + current), feet.z); num = Mathf.Min(num, Vector3.Distance(item, val)); } return num; } internal static string? ThroughStructure(List<Vector3> arc, Vector3 exit, Vector3 land, Vector3? target, float r) { //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_0023: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0054: 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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00e4: Unknown result type (might be due to invalid IL or missing references) int structureMask = LeviathanBrain.StructureMask; if (structureMask == 0) { return null; } float num = 0.4f * r; for (int i = 0; i < arc.Count; i += 2) { Vector3 val = arc[i]; if (Flat(val, exit) < 1.5f * r || Flat(val, land) < 1.5f * r || (target.HasValue && Vector3.Distance(val, target.Value + Vector3.up * 0.9f) < r + 2f)) { continue; } LeviathanCost.Rays++; int num2 = Physics.OverlapSphereNonAlloc(val, num, overlap, structureMask, (QueryTriggerInteraction)1); for (int j = 0; j < num2; j++) { Collider val2 = overlap[j]; float y; if ((Object)(object)val2 != (Object)null && ManMade(val2)) { return $"its arc would go through \"{((Object)val2).name}\" ({LeviathanSurfaces.Of(val2, val)}) {val.y - (LeviathanPath.GroundAt(val, out y) ? y : val.y):0} m up"; } } } return null; } internal static bool ManMade(Collider c) { if (!(c is TerrainCollider) && !LeviathanPath.IsNaturalGround(c) && !((Component)c).CompareTag("Tree")) { return !LeviathanPath.Small(c); } return false; } private static IEnumerable<bool> LeapPlan(PlanJob job, Vector3 exit, Vector3 p0, Vector3 dir, float ahead, float d1, float depth, bool fromHead, float gH, LeapArc arc) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Vector3 start = job.Start; Vector3 target = job.Target; LeviathanBrain worm = job.Worm; bool fitOnly = job.FitOnly; float r = worm.Radius; float after = r + Mathf.Max(LeviathanTuning.RecoveryDepth.Current, LeviathanTuning.LeapDiveDepth.Current + 5f); float under = r + LeviathanTuning.LeapDiveDepth.Current; if (fitOnly && !fromHead) { job.Succeed(Fits); yield break; } Points pts = ne