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 Heel v2.1.0
plugins\Heel.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Heel.Commands; using Heel.Config; using Heel.Follow; using Heel.Safety; using Heel.Server; using Heel.Util; using Microsoft.CodeAnalysis; using UnityEngine; [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("Heel")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("Heel")] [assembly: AssemblyTitle("Heel")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Heel { internal static class ModInfo { internal const string Guid = "dafini.heel"; internal const string Name = "Heel"; internal const string Version = "2.1.0"; } [BepInPlugin("dafini.heel", "Heel", "2.1.0")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "dafini.heel"; public const string PluginName = "Heel"; public const string PluginVersion = "2.1.0"; private Harmony _harmony; private bool _driverChosen; private void Update() { if (_driverChosen) { Warden.Tick(); return; } ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null)) { _driverChosen = true; if (Warden.ShouldRun(instance.IsDedicated(), HeelConfig.ServerSide.Value)) { Warden.Begin(); } } } private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log.Bind(((BaseUnityPlugin)this).Logger); HeelConfig.Bind(((BaseUnityPlugin)this).Config); try { _harmony = new Harmony("dafini.heel"); _harmony.PatchAll(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Heel failed to patch and is disabled for this session: " + ex)); return; } HeelCommand.Register(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Heel 2.1.0 loaded. Followers adrift beyond " + $"{HeelConfig.AdriftDistance.Value}m, or stalled, are brought to you after " + $"{HeelConfig.CatchUpDelay.Value}s.")); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Recall.ResetForTests(); } } } namespace Heel.Util { internal static class Log { private static ManualLogSource _source; internal static void Bind(ManualLogSource source) { _source = source; } internal static void Info(string message) { ManualLogSource source = _source; if (source != null) { source.LogInfo((object)message); } } internal static void Warn(string message) { ManualLogSource source = _source; if (source != null) { source.LogWarning((object)message); } } internal static void Error(string message) { ManualLogSource source = _source; if (source != null) { source.LogError((object)message); } } internal static void Debug(string message) { if (HeelConfig.Verbose != null && HeelConfig.Verbose.Value) { ManualLogSource source = _source; if (source != null) { source.LogInfo((object)message); } } } } } namespace Heel.Server { internal sealed class Bearing { private sealed class Track { internal Vector3 Where; internal Vector3 Facing; internal float Seen; } internal const float Meaningful = 1f; private readonly Dictionary<string, Track> _track = new Dictionary<string, Track>(StringComparer.Ordinal); internal int Count => _track.Count; internal void Clear() { _track.Clear(); } internal static Vector3 From(Vector3 previous, Vector3 current, Vector3 lastKnown) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(current.x - previous.x, 0f, current.z - previous.z); if (((Vector3)(ref val)).sqrMagnitude >= 1f) { return ((Vector3)(ref val)).normalized; } Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(lastKnown.x, 0f, lastKnown.z); if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0001f)) { return ((Vector3)(ref val2)).normalized; } return Vector3.forward; } internal Vector3 Of(string player, Vector3 where, float now) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0075: 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_002c: 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_0048: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(player)) { return Vector3.forward; } if (!_track.TryGetValue(player, out var value)) { _track[player] = new Track { Where = where, Facing = Vector3.forward, Seen = now }; return Vector3.forward; } value.Facing = From(value.Where, where, value.Facing); value.Where = where; value.Seen = now; return value.Facing; } internal int Forget(float now, float after) { List<string> list = new List<string>(); foreach (KeyValuePair<string, Track> item in _track) { if (now - item.Value.Seen > after) { list.Add(item.Key); } } foreach (string item2 in list) { _track.Remove(item2); } return list.Count; } } internal sealed class Berth { private sealed class Landing { internal int Next; internal float Last; } private readonly Dictionary<string, Landing> _by = new Dictionary<string, Landing>(StringComparer.Ordinal); internal int Count => _by.Count; internal void Clear() { _by.Clear(); } internal bool Ready(string player, float now, float spacing, float quiet, out int slot) { slot = 0; if (string.IsNullOrEmpty(player)) { return false; } if (!_by.TryGetValue(player, out var value)) { value = new Landing(); _by[player] = value; } else if (now - value.Last > quiet) { value.Next = 0; } else if (now - value.Last < spacing) { return false; } slot = value.Next; value.Next++; value.Last = now; return true; } internal int Forget(float now, float after) { List<string> list = new List<string>(); foreach (KeyValuePair<string, Landing> item in _by) { if (now - item.Value.Last > after) { list.Add(item.Key); } } foreach (string item2 in list) { _by.Remove(item2); } return list.Count; } } internal static class Claim { internal enum Move { Take, Mine, Busy } internal static Move Decide(bool ownedByUs, bool ownedByAnother) { if (ownedByUs) { return Move.Mine; } if (!ownedByAnother) { return Move.Take; } return Move.Busy; } internal static bool MayWrite(Move move) { return move != Move.Busy; } } internal static class Drop { internal static Vector3 Spot(Vector3 standingOn, Vector3 forward, int index, int count, float minRadius, float ringStep, float halfWidthDeg) { //IL_0002: 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_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_0015: 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_0022: 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_002f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Arc.Slot(index, count, forward, minRadius, ringStep, halfWidthDeg); return new Vector3(standingOn.x + val.x, standingOn.y, standingOn.z + val.z); } internal static Vector3 Spot(Vector3 standingOn, Vector3 forward, int arrival, float minRadius, float ringStep, float halfWidthDeg) { //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_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_0020: 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_002d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Arc.OpenSlot(arrival, forward, minRadius, ringStep, halfWidthDeg); return new Vector3(standingOn.x + val.x, standingOn.y, standingOn.z + val.z); } internal static Verdict Judge(Vector3 standingOn, float waterLevel, float clearance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Landing.Judge(floorFound: true, standingOn.y, waterLevel, clearance, warded: false); } } internal static class Herd { internal enum Verdict { Follower, Wild, Staying, OwnerAway } internal static Verdict Judge(bool tamed, string followName, bool connected) { if (!tamed) { return Verdict.Wild; } if (string.IsNullOrEmpty(followName)) { return Verdict.Staying; } if (!connected) { return Verdict.OwnerAway; } return Verdict.Follower; } internal static bool IsFollower(Verdict verdict) { return verdict == Verdict.Follower; } internal static bool SamePlayer(string a, string b) { if (!string.IsNullOrEmpty(a)) { return string.Equals(a, b, StringComparison.Ordinal); } return false; } } internal sealed class Peers { internal struct Who { internal long Uid; internal string Name; internal Vector3 Where; internal Vector3 Facing; } internal enum Holder { Nobody, ItsOwnPlayer, Another } private readonly List<Who> _who = new List<Who>(); internal IList<Who> All => _who; internal int Count => _who.Count; internal void Clear() { _who.Clear(); } internal void Add(Who who) { _who.Add(who); } internal bool Find(string name, out Who found) { found = default(Who); foreach (Who item in _who) { if (Herd.SamePlayer(item.Name, name)) { found = item; return true; } } return false; } internal Holder WhoHolds(long owner, string followName) { if (owner == 0L) { return Holder.Nobody; } foreach (Who item in _who) { if (item.Uid == owner) { return Herd.SamePlayer(item.Name, followName) ? Holder.ItsOwnPlayer : Holder.Another; } } return Holder.Nobody; } internal float NearestTo(Vector3 point) { //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) float num = float.PositiveInfinity; foreach (Who item in _who) { float num2 = Vector3.Distance(point, item.Where); if (num2 < num) { num = num2; } } return num; } } internal static class Sweep { internal struct Cursor { internal int Prefab; internal int Index; internal int Passes; } internal const int Slice = 128; internal static Cursor Next(Cursor at, int prefabCount) { if (prefabCount <= 0) { return default(Cursor); } int num = at.Prefab + 1; int num2 = at.Passes; if (num >= prefabCount) { num = 0; num2++; } return new Cursor { Prefab = num, Index = 0, Passes = num2 }; } internal static Cursor Continued(Cursor at, int reachedIndex) { return new Cursor { Prefab = at.Prefab, Index = reachedIndex, Passes = at.Passes }; } internal static bool Valid(Cursor at, int prefabCount) { if (prefabCount > 0 && at.Prefab >= 0 && at.Prefab < prefabCount) { return at.Index >= 0; } return false; } internal static List<string> Tameables(IEnumerable<GameObject> prefabs) { List<string> list = new List<string>(); if (prefabs == null) { return list; } foreach (GameObject prefab in prefabs) { if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<Tameable>() == (Object)null)) { list.Add(((Object)prefab).name); } } return list; } } internal static class Warden { internal const float Interval = 1f; private static float _next; private static Sweep.Cursor _cursor; private static List<string> _tameables; private static readonly Watch Clock = new Watch(); private static readonly Peers Present = new Peers(); private static readonly Berth Arrivals = new Berth(); private static readonly Bearing Headings = new Bearing(); private static readonly List<ZDO> Batch = new List<ZDO>(); internal static bool Running { get; private set; } internal static int Lifted { get; private set; } internal static int Considered { get; private set; } internal static int LeftBusy { get; private set; } internal static int HeldForSpacing { get; private set; } internal static void ResetForTests() { Running = false; Lifted = 0; Considered = 0; LeftBusy = 0; HeldForSpacing = 0; _next = 0f; _cursor = default(Sweep.Cursor); _tameables = null; Clock.Clear(); Present.Clear(); Arrivals.Clear(); Headings.Clear(); } internal static bool ShouldRun(bool dedicated, bool enabled) { return dedicated && enabled; } internal static void Begin() { Running = true; Log.Info("Heel is running server-side. Clients do not need this mod installed. Hazard avoidance is client-only and is not active."); } internal static void Tick() { if (Running && !(Time.time < _next)) { _next = Time.time + 1f; ZDOMan instance = ZDOMan.instance; ZNet instance2 = ZNet.instance; if (instance != null && !((Object)(object)instance2 == (Object)null) && Gather(instance2) && Prefabs()) { Slice(instance); float after = HeelConfig.ForgetAfter.Value + 60f; Clock.Forget(Time.time, after); Arrivals.Forget(Time.time, after); Headings.Forget(Time.time, after); } } } private static bool Gather(ZNet net) { //IL_0061: 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_0079: 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_0088: Unknown result type (might be due to invalid IL or missing references) Present.Clear(); List<ZNetPeer> peers = net.GetPeers(); if (peers == null) { return false; } foreach (ZNetPeer item in peers) { if (item != null && !string.IsNullOrEmpty(item.m_playerName)) { Present.Add(new Peers.Who { Uid = item.m_uid, Name = item.m_playerName, Where = item.m_refPos, Facing = Headings.Of(item.m_playerName, item.m_refPos, Time.time) }); } } return Present.Count > 0; } private static bool Prefabs() { if (_tameables != null) { return _tameables.Count > 0; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return false; } _tameables = Sweep.Tameables(instance.m_prefabs); Log.Info("Heel found " + _tameables.Count + " tameable prefab(s) to watch."); return _tameables.Count > 0; } private static void Slice(ZDOMan zdoMan) { if (!Sweep.Valid(_cursor, _tameables.Count)) { _cursor = default(Sweep.Cursor); } string text = _tameables[_cursor.Prefab]; Batch.Clear(); int index = _cursor.Index; bool allZDOsWithPrefabIterative = zdoMan.GetAllZDOsWithPrefabIterative(text, Batch, ref index); foreach (ZDO item in Batch) { Consider(zdoMan, item); } _cursor = (allZDOsWithPrefabIterative ? Sweep.Next(_cursor, _tameables.Count) : Sweep.Continued(_cursor, index)); } private unsafe static void Consider(ZDOMan zdoMan, ZDO zdo) { //IL_004b: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0161: 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_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) if (zdo == null) { return; } string text = zdo.GetString(ZDOVars.s_follow, string.Empty); bool tamed = zdo.GetBool(ZDOVars.s_tamed, false); Peers.Who found; bool connected = Present.Find(text, out found); if (!Herd.IsFollower(Herd.Judge(tamed, text, connected))) { return; } Considered++; float distance = Vector3.Distance(zdo.GetPosition(), found.Where); if (!Clock.Due(zdo.m_uid, distance, Time.time, HeelConfig.WithYouDistance.Value, HeelConfig.AdriftDistance.Value, HeelConfig.ClosingBy.Value, HeelConfig.CatchUpDelay.Value, out var standing)) { return; } Claim.Move move = Claim.Decide(zdo.GetOwner() == ZDOMan.GetSessionID(), Present.WhoHolds(zdo.GetOwner(), text) == Peers.Holder.Another); if (!Claim.MayWrite(move)) { LeftBusy++; return; } Verdict verdict = Drop.Judge(found.Where, ZoneSystem.instance.m_waterLevel, HeelConfig.WaterClearance.Value); if (verdict != Verdict.Ok) { Log.Debug("heel/server: not lifting " + text + "'s follower - " + verdict); return; } if (!Arrivals.Ready(text, Time.time, HeelConfig.ArrivalSpacing.Value, HeelConfig.CatchUpDelay.Value, out var slot)) { HeldForSpacing++; return; } Vector3 val = Drop.Spot(found.Where, found.Facing, slot, HeelConfig.MinRadius.Value, HeelConfig.RingStep.Value, HeelConfig.ArcHalfWidth.Value); zdo.SetOwner(ZDOMan.GetSessionID()); zdo.SetPosition(val); zdoMan.ForceSendZDO(zdo.m_uid); Clock.Settled(zdo.m_uid); Lifted++; string[] obj = new string[12] { "heel/server: lifted a follower of ", text, " (", standing.ToString(), ", ", distance.ToString("0"), "m, ", move.ToString(), ", slot ", slot.ToString(), ") to ", null }; Vector3 val2 = val; obj[11] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString(); Log.Debug(string.Concat(obj)); } internal static List<string> Report() { List<string> list = new List<string> { "--- heel (server) ---" }; if (!Running) { list.Add(" not running - this machine is not a dedicated server, so the client path is in charge"); return list; } list.Add(" watching " + ((_tameables != null) ? _tameables.Count : 0) + " tameable prefab(s), cursor at " + _cursor.Prefab + "/" + _cursor.Index + ", " + _cursor.Passes + " full pass(es)"); list.Add(" players " + Present.Count + " connected"); list.Add(" followers " + Considered + " considered, " + Lifted + " lifted, " + LeftBusy + " left in somebody else's world, " + HeldForSpacing + " held back a tick for spacing"); list.Add(" clocks " + Clock.Count + " follower(s) being timed"); list.Add(" arrivals " + Arrivals.Count + " player(s) mid-landing, " + Headings.Count + " heading(s) tracked"); list.Add(" NOTE hazard avoidance is client-only and is not running here"); return list; } } internal sealed class Watch { private sealed class Since { internal float Waiting; internal float LastDistance = float.PositiveInfinity; internal float Seen; } private readonly Dictionary<ZDOID, Since> _since = new Dictionary<ZDOID, Since>(); internal int Count => _since.Count; internal void Clear() { _since.Clear(); } internal bool Due(ZDOID id, float distance, float now, float withYou, float adrift, float closingBy, float delay, out Standing standing) { //IL_0006: 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) if (!_since.TryGetValue(id, out var value)) { value = new Since(); _since[id] = value; } standing = Progress.Judge(distance, value.LastDistance, withYou, adrift, closingBy); value.LastDistance = distance; value.Seen = now; if (!Progress.Waiting(standing)) { value.Waiting = 0f; return false; } if (value.Waiting <= 0f) { value.Waiting = now; } return Progress.Elapsed(value.Waiting, now, delay); } internal void Settled(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (_since.TryGetValue(id, out var value)) { value.Waiting = 0f; value.LastDistance = float.PositiveInfinity; } } internal int Forget(float now, float after) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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) List<ZDOID> list = new List<ZDOID>(); foreach (KeyValuePair<ZDOID, Since> item in _since) { if (now - item.Value.Seen > after) { list.Add(item.Key); } } foreach (ZDOID item2 in list) { _since.Remove(item2); } return list.Count; } } } namespace Heel.Safety { internal static class Ground { private static int _smokeMask = -1; private static int SmokeMask { get { if (_smokeMask == -1) { _smokeMask = LayerMask.GetMask(new string[1] { "smoke" }); } return _smokeMask; } } internal static void ResetForTests() { _smokeMask = -1; } internal static Peril At(Vector3 point) { //IL_000c: 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) if (HeelConfig.AvoidSmoke.Value && InSmoke(point)) { return Peril.Smoke; } if (HeelConfig.AvoidTraps.Value && InTrap(point)) { return Peril.Trap; } return Peril.None; } internal static bool InSmoke(Vector3 point) { //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_000b: 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) try { return Physics.CheckSphere(point + Vector3.up * 0.5f, 0.5f, SmokeMask); } catch (Exception) { return false; } } internal static bool InTrap(Vector3 point) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) float value = HeelConfig.TrapMargin.Value; Collider[] array = Physics.OverlapSphere(point, value); foreach (Collider val in array) { if (!((Object)(object)val == (Object)null)) { Aoe componentInParent = ((Component)val).GetComponentInParent<Aoe>(); if (!((Object)(object)componentInParent == (Object)null) && Hazard.Harmful(componentInParent.m_hitCharacters, componentInParent.m_hitFriendly, Damage(componentInParent)) && Hazard.Reaches(Vector3.Distance(point, ((Component)componentInParent).transform.position), componentInParent.m_radius, value)) { return true; } } } return false; } internal static float Damage(Aoe aoe) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_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_0023: 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_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_003f: 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_004d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)aoe == (Object)null) { return 0f; } DamageTypes damage = aoe.m_damage; return damage.m_damage + damage.m_blunt + damage.m_slash + damage.m_pierce + damage.m_fire + damage.m_frost + damage.m_lightning + damage.m_poison + damage.m_spirit; } internal static IEnumerable<Peril> All(Vector3 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) List<Peril> list = new List<Peril>(); if (InSmoke(point)) { list.Add(Peril.Smoke); } if (InTrap(point)) { list.Add(Peril.Trap); } return list; } } internal enum Peril { None, Fire, Lava, Water, Smoke, Trap } internal static class Hazard { internal const float Margin = 1.5f; internal static bool Harmful(bool hitsCharacters, bool hitsFriendly, float damage) { if (hitsCharacters && hitsFriendly) { return damage > 0f; } return false; } internal static bool Reaches(float distance, float hazardRadius, float margin) { return distance <= hazardRadius + margin; } internal static void Switches(bool fire, bool lava, bool water, out bool avoidFire, out bool avoidLava, out bool avoidWater) { avoidFire = fire; avoidLava = lava; avoidWater = water; } internal static bool Guards(bool tamed, bool alive, bool hasFollowTarget) { if (tamed && alive) { return !hasFollowTarget; } return false; } } internal static class Minding { [HarmonyPatch(typeof(BaseAI), "IsValidRandomMovePoint")] internal static class Validate { [HarmonyPostfix] private static void Postfix(BaseAI __instance, Vector3 point, ref bool __result) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!__result || HeelConfig.Enabled == null || !HeelConfig.Enabled.Value || !HeelConfig.MindHazards.Value) { return; } try { if (Applies(__instance) && Ground.At(point) != Peril.None) { __result = false; Refused++; } } catch (Exception ex) { Log.Error("Heel hit an error judging a wander point: " + ex); } } } internal const float SweepInterval = 2f; private static float _nextSweep; internal static int Guarded { get; private set; } internal static int Nudged { get; private set; } internal static int Refused { get; private set; } internal static void ResetForTests() { _nextSweep = 0f; Guarded = 0; Nudged = 0; Refused = 0; Ground.ResetForTests(); } internal static void Tick(float now) { if (HeelConfig.Enabled == null || !HeelConfig.Enabled.Value || !HeelConfig.MindHazards.Value || now < _nextSweep) { return; } _nextSweep = now + 2f; List<Character> allCharacters = Character.GetAllCharacters(); if (allCharacters == null) { return; } int num = 0; foreach (Character item in allCharacters) { if (!((Object)(object)item == (Object)null)) { MonsterAI component = ((Component)item).GetComponent<MonsterAI>(); if (!((Object)(object)component == (Object)null) && Hazard.Guards(item.IsTamed(), !item.IsDead(), (Object)(object)component.GetFollowTarget() != (Object)null)) { num++; Arm(component); Rescue(item, component); } } } Guarded = num; } internal static void Arm(MonsterAI ai) { Hazard.Switches(HeelConfig.AvoidFire.Value, HeelConfig.AvoidLava.Value, HeelConfig.AvoidWater.Value, out var avoidFire, out var avoidLava, out var avoidWater); if (avoidFire) { ((BaseAI)ai).m_avoidFire = true; } if (avoidLava) { ((BaseAI)ai).m_avoidLava = true; } if (avoidWater) { ((BaseAI)ai).m_avoidWater = true; } } internal static void Rescue(Character character, MonsterAI ai) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Peril peril = Ground.At(((Component)character).transform.position); if (peril != Peril.None) { ((BaseAI)ai).ResetRandomMovement(); Nudged++; Log.Debug($"heel: {character.m_name} is standing in {peril}, sending it off"); } } internal static bool Applies(BaseAI ai) { MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null); if ((Object)(object)val == (Object)null) { return false; } Character component = ((Component)ai).GetComponent<Character>(); if ((Object)(object)component == (Object)null) { return false; } return Hazard.Guards(component.IsTamed(), !component.IsDead(), (Object)(object)val.GetFollowTarget() != (Object)null); } } } namespace Heel.Patches { [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerUpdatePatch { private static bool _shouted; [HarmonyPostfix] private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { Recall.Tick(__instance, Time.time); Minding.Tick(Time.time); } catch (Exception ex) { if (!_shouted) { _shouted = true; Log.Error("Heel hit an error and will stay quiet about repeats: " + ex); } } } } } namespace Heel.Follow { internal static class Arc { internal const int PerRing = 5; internal static Vector3 Slot(int index, int count, Vector3 forward, float minRadius, float ringStep, float halfWidthDeg) { //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_0040: Unknown result type (might be due to invalid IL or missing references) if (index < 0) { index = 0; } if (count < 1) { count = 1; } Vector3 flat = Flatten(forward); int num = index / 5; int index2 = index % 5; int num2 = count - num * 5; if (num2 > 5) { num2 = 5; } if (num2 < 1) { num2 = 1; } float angle = Spread(index2, num2, halfWidthDeg); float radius = minRadius + (float)num * ringStep; return Place(flat, angle, radius); } internal static Vector3 OpenSlot(int arrival, Vector3 forward, float minRadius, float ringStep, float halfWidthDeg) { //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_002b: Unknown result type (might be due to invalid IL or missing references) if (arrival < 0) { arrival = 0; } int num = arrival / 5; float angle = Spread(CentreOut(arrival % 5), 5, halfWidthDeg); float radius = minRadius + (float)num * ringStep; return Place(Flatten(forward), angle, radius); } internal static int CentreOut(int within) { if (within < 0) { within = 0; } if (within >= 5) { within = 4; } int num = 2; int num2 = (within + 1) / 2; if (within % 2 != 1) { return num + num2; } return num - num2; } private static Vector3 Flatten(Vector3 forward) { //IL_0002: 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_003e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(forward.x, 0f, forward.z); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { ((Vector3)(ref val))..ctor(0f, 0f, 1f); } return ((Vector3)(ref val)).normalized; } private static Vector3 Place(Vector3 flat, float angle, float radius) { //IL_0000: 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_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_001f: Unknown result type (might be due to invalid IL or missing references) return RotateY(new Vector3(0f - flat.x, 0f, 0f - flat.z), angle) * radius; } internal static float Spread(int index, int count, float halfWidthDeg) { if (count <= 1) { return 0f; } return ((float)index / (float)(count - 1) - 0.5f) * 2f * halfWidthDeg; } internal static Vector3 RotateY(Vector3 v, float degrees) { //IL_0014: 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_0025: 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_0034: 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) float num = degrees * (MathF.PI / 180f); float num2 = Mathf.Cos(num); float num3 = Mathf.Sin(num); return new Vector3(v.x * num2 + v.z * num3, v.y, (0f - v.x) * num3 + v.z * num2); } internal static float OuterRadius(int count, float minRadius, float ringStep) { if (count < 1) { count = 1; } int num = (count - 1) / 5; return minRadius + (float)num * ringStep; } } internal enum Claim { Yours, Untamed, SomeoneElses, NotFollowing, Gone } internal static class Followers { internal static Claim Judge(bool hasAi, bool alive, bool tamed, bool followingMe) { if (!hasAi || !alive) { return Claim.Gone; } if (!tamed) { return Claim.Untamed; } if (!followingMe) { return Claim.SomeoneElses; } return Claim.Yours; } internal static Claim Judge(bool hasAi, bool alive, bool tamed, bool hasFollowTarget, bool followingMe) { if (!hasAi || !alive) { return Claim.Gone; } if (!tamed) { return Claim.Untamed; } if (followingMe) { return Claim.Yours; } if (!hasFollowTarget) { return Claim.NotFollowing; } return Claim.SomeoneElses; } internal static Claim Of(Character character, Player player) { if ((Object)(object)character == (Object)null || (Object)(object)player == (Object)null) { return Claim.Gone; } MonsterAI component = ((Component)character).GetComponent<MonsterAI>(); if ((Object)(object)component == (Object)null) { return Claim.Gone; } GameObject followTarget = component.GetFollowTarget(); bool followingMe = (Object)(object)followTarget != (Object)null && followTarget == ((Component)player).gameObject; return Judge(hasAi: true, !character.IsDead(), character.IsTamed(), (Object)(object)followTarget != (Object)null, followingMe); } } internal enum Verdict { Ok, NoGround, Water, Warded } internal static class Landing { internal static bool Footing(bool floorFound, float floorHeight, float waterLevel, float clearance) { if (floorFound) { return floorHeight >= waterLevel + clearance; } return false; } internal static Verdict Judge(bool floorFound, float floorHeight, float waterLevel, float clearance, bool warded) { if (!floorFound) { return Verdict.NoGround; } if (!Footing(floorFound: true, floorHeight, waterLevel, clearance)) { return Verdict.Water; } if (warded) { return Verdict.Warded; } return Verdict.Ok; } internal static Vector3 OnFloor(Vector3 candidate, float floorHeight, float lift) { //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) //IL_000f: Unknown result type (might be due to invalid IL or missing references) return new Vector3(candidate.x, floorHeight + lift, candidate.z); } } internal enum Standing { WithYou, Closing, Stalled, Adrift } internal static class Progress { internal static Standing Judge(float distance, float previousDistance, float withYou, float adrift, float closingBy) { if (distance >= adrift) { return Standing.Adrift; } if (distance <= withYou) { return Standing.WithYou; } if (previousDistance - distance >= closingBy) { return Standing.Closing; } return Standing.Stalled; } internal static bool Waiting(Standing standing) { if (standing != Standing.Adrift) { return standing == Standing.Stalled; } return true; } internal static bool Elapsed(float waitingSince, float now, float delay) { if (waitingSince <= 0f) { return false; } return now - waitingSince >= delay; } } internal static class Recall { internal const float SampleInterval = 1f; private static float _nextSample; private static float _nextRelease; private static readonly List<Enrolled> Ready = new List<Enrolled>(); private static int _slot; private static int _batch; internal static int Waiting { get; private set; } internal static int Arrived { get; private set; } internal static void ResetForTests() { _nextSample = 0f; _nextRelease = 0f; _slot = 0; _batch = 0; Ready.Clear(); Roster.Clear(); Waiting = 0; Arrived = 0; } internal static void Tick(Player player, float now) { if (!((Object)(object)player == (Object)null) && HeelConfig.Enabled != null && HeelConfig.Enabled.Value) { if (now >= _nextSample) { _nextSample = now + 1f; Sample(player, now); } Release(player, now); } } private static void Sample(Player player, float now) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) List<Character> allCharacters = Character.GetAllCharacters(); if (allCharacters == null) { return; } Vector3 position = ((Component)player).transform.position; foreach (Character item in allCharacters) { if ((Object)(object)item == (Object)null) { continue; } ZNetView component = ((Component)item).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { continue; } ZDOID uid = component.GetZDO().m_uid; if (Followers.Of(item, player) != Claim.Yours) { if (Roster.Get(uid) != null) { Roster.Strike(uid); } } else { Enrolled entry = Roster.Enrol(uid, item.m_name, now); float distance = Vector3.Distance(position, ((Component)item).transform.position); Consider(entry, distance, now, Fighting(item)); } } Roster.Forget(now, HeelConfig.ForgetAfter.Value); Sweep(now); } internal static void Consider(Enrolled entry, float distance, float now, bool fighting) { Standing standing = Progress.Judge(distance, entry.LastDistance, HeelConfig.WithYouDistance.Value, HeelConfig.AdriftDistance.Value, HeelConfig.ClosingBy.Value); entry.LastDistance = distance; bool flag = fighting && HeelConfig.HoldWhileFighting.Value; if (!Progress.Waiting(standing) || flag) { entry.WaitingSince = 0f; entry.Queued = false; } else if (entry.WaitingSince <= 0f) { entry.WaitingSince = now; } } private static void Sweep(float now) { int num = 0; foreach (Enrolled item in Roster.All) { if (item.WaitingSince > 0f) { num++; } if (!item.Queued && Progress.Elapsed(item.WaitingSince, now, HeelConfig.CatchUpDelay.Value)) { item.Queued = true; Ready.Add(item); } } Waiting = num; } private static bool Fighting(Character character) { MonsterAI component = ((Component)character).GetComponent<MonsterAI>(); if ((Object)(object)component != (Object)null) { return ((BaseAI)component).IsAlerted(); } return false; } private static void Release(Player player, float now) { if (Ready.Count == 0) { _slot = 0; _batch = 0; } else if (!(now < _nextRelease)) { _nextRelease = now + HeelConfig.ArrivalSpacing.Value; if (_batch < Ready.Count + _slot) { _batch = Ready.Count + _slot; } Enrolled enrolled = Ready[0]; Ready.RemoveAt(0); if (Land(player, enrolled, _slot, _batch)) { _slot++; enrolled.WaitingSince = 0f; enrolled.Queued = false; enrolled.LastDistance = float.MaxValue; Arrived++; } else { enrolled.Queued = true; Ready.Add(enrolled); } } } private static bool Land(Player player, Enrolled entry, int slot, int batch) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return false; } Vector3 position = ((Component)player).transform.position; Vector3 forward = ((Component)player).transform.forward; float floorHeight = default(float); for (int i = 0; i < 6; i++) { int num = slot + i * 5; Vector3 val = Arc.Slot(num, batch + i * 5, forward, HeelConfig.MinRadius.Value, HeelConfig.RingStep.Value, HeelConfig.ArcHalfWidth.Value); Vector3 val2 = position + val; if (instance.FindFloor(val2, ref floorHeight)) { bool warded = !PrivateArea.CheckAccess(val2, 0f, false, true); Verdict verdict = Landing.Judge(floorFound: true, floorHeight, instance.m_waterLevel, HeelConfig.WaterClearance.Value, warded); if (verdict == Verdict.Ok) { return Relocate.Move(entry.Id, Landing.OnFloor(val2, floorHeight, 0.2f), entry.Name); } Log.Debug($"heel: slot {num} for {entry.Name} rejected - {verdict}"); } } Log.Debug("heel: nowhere safe behind you for " + entry.Name + " yet"); return false; } } internal enum Route { Loaded, ByRecord, Unreachable } internal static class Relocate { internal static int Moved { get; private set; } internal static int Refused { get; private set; } internal static void ResetForTests() { Moved = 0; Refused = 0; } internal static Route Choose(bool objectLoaded, bool recordFound) { if (objectLoaded) { return Route.Loaded; } if (recordFound) { return Route.ByRecord; } return Route.Unreachable; } internal static bool Move(ZDOID id, Vector3 to, string name) { //IL_0025: 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) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; ZDOMan instance2 = ZDOMan.instance; if ((Object)(object)instance == (Object)null || instance2 == null) { return Refuse(name, "the world is not up"); } GameObject val = instance.FindInstance(id); ZDO zDO = instance2.GetZDO(id); return Choose((Object)(object)val != (Object)null, zDO != null) switch { Route.Loaded => MoveObject(val, to, name), Route.ByRecord => MoveRecord(zDO, to, name), _ => Refuse(name, "neither loaded nor in the record table"), }; } private static bool MoveObject(GameObject instance, Vector3 to, string name) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) ZNetView component = instance.GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return Refuse(name, "no valid network view"); } component.ClaimOwnership(); instance.transform.position = to; Rigidbody component2 = instance.GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.velocity = Vector3.zero; component2.angularVelocity = Vector3.zero; } Moved++; Log.Debug($"heel: moved {name} (loaded) to {to}"); return true; } private static bool MoveRecord(ZDO record, Vector3 to, string name) { //IL_000c: 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) record.SetOwner(ZDOMan.GetSessionID()); record.SetPosition(to); Moved++; Log.Debug($"heel: moved {name} (by record) to {to}"); return true; } private static bool Refuse(string name, string why) { Refused++; Log.Debug("heel: left " + name + " alone - " + why); return false; } } internal sealed class Enrolled { internal ZDOID Id; internal string Name = string.Empty; internal float LastDistance = float.MaxValue; internal float WaitingSince; internal float LastSeen; internal bool Queued; } internal static class Roster { private static readonly Dictionary<ZDOID, Enrolled> Book = new Dictionary<ZDOID, Enrolled>(); internal static int Count => Book.Count; internal static IEnumerable<Enrolled> All => Book.Values; internal static void Clear() { Book.Clear(); } internal static Enrolled Get(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!Book.TryGetValue(id, out var value)) { return null; } return value; } internal static Enrolled Enrol(ZDOID id, string name, float now) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) if (!Book.TryGetValue(id, out var value)) { value = new Enrolled { Id = id }; Book[id] = value; } if (!string.IsNullOrEmpty(name)) { value.Name = name; } value.LastSeen = now; return value; } internal static void Strike(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) Book.Remove(id); } internal static int Forget(float now, float after) { //IL_0036: 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_006e: 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) List<ZDOID> list = null; foreach (KeyValuePair<ZDOID, Enrolled> item in Book) { if (!(now - item.Value.LastSeen < after)) { list = list ?? new List<ZDOID>(); list.Add(item.Key); } } if (list == null) { return 0; } foreach (ZDOID item2 in list) { Book.Remove(item2); } return list.Count; } internal static bool Stale(float lastSeen, float now, float after) { return now - lastSeen >= after; } } } namespace Heel.Config { internal static class HeelConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> CatchUpDelay; internal static ConfigEntry<float> AdriftDistance; internal static ConfigEntry<float> WithYouDistance; internal static ConfigEntry<float> ClosingBy; internal static ConfigEntry<float> ArrivalSpacing; internal static ConfigEntry<float> MinRadius; internal static ConfigEntry<float> RingStep; internal static ConfigEntry<float> ArcHalfWidth; internal static ConfigEntry<float> WaterClearance; internal static ConfigEntry<bool> HoldWhileFighting; internal static ConfigEntry<float> ForgetAfter; internal static ConfigEntry<bool> MindHazards; internal static ConfigEntry<bool> AvoidFire; internal static ConfigEntry<bool> AvoidLava; internal static ConfigEntry<bool> AvoidWater; internal static ConfigEntry<bool> AvoidSmoke; internal static ConfigEntry<bool> AvoidTraps; internal static ConfigEntry<float> TrapMargin; internal static ConfigEntry<bool> Verbose; internal static ConfigEntry<bool> ServerSide; internal static void Bind(ConfigFile cfg) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Expected O, but got Unknown //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Expected O, but got Unknown Enabled = cfg.Bind<bool>("General", "Enabled", true, "Master switch. Off means this mod does nothing at all."); CatchUpDelay = cfg.Bind<float>("Timing", "CatchUpDelay", 25f, new ConfigDescription("Seconds a follower must be adrift or stalled before it is brought to you. The delay is the point: it gives them a chance to arrive under their own steam, and it keeps a portal exit clear while you walk away from it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 300f), Array.Empty<object>())); ArrivalSpacing = cfg.Bind<float>("Timing", "ArrivalSpacing", 0.5f, new ConfigDescription("Seconds between one arrival and the next. With a large pack this is what stops twelve animals materialising in the same instant. Applies to the server path too as of 2.1.0, which is where a large pack actually shows up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); AdriftDistance = cfg.Bind<float>("Distance", "AdriftDistance", 40f, new ConfigDescription("Beyond this many metres a follower is considered unable to catch up, and the clock runs no matter what it is doing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 500f), Array.Empty<object>())); WithYouDistance = cfg.Bind<float>("Distance", "WithYouDistance", 12f, new ConfigDescription("Within this many metres a follower counts as being with you and the clock is cleared. Normal following sits well inside it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 40f), Array.Empty<object>())); ClosingBy = cfg.Bind<float>("Distance", "ClosingBy", 0.5f, new ConfigDescription("Metres a follower must close per sample to count as making progress. This is what tells a wolf jogging up behind you from a wolf in a pit: both are at the same distance, only one of them is getting nearer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); MinRadius = cfg.Bind<float>("Placement", "MinRadius", 5f, new ConfigDescription("How far behind you the first ring of arrivals lands.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>())); RingStep = cfg.Bind<float>("Placement", "RingStep", 3.5f, new ConfigDescription("How much further out each additional ring sits. A ring holds five.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); ArcHalfWidth = cfg.Bind<float>("Placement", "ArcHalfWidth", 65f, new ConfigDescription("Half the width of the arrival arc, in degrees either side of dead-behind. Must stay under 90 or arrivals start appearing beside and then in front of you, which is the thing the arc exists to prevent.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 89f), Array.Empty<object>())); WaterClearance = cfg.Bind<float>("Safety", "WaterClearance", 0.5f, new ConfigDescription("Metres of dry ground a landing spot must have above the water line. The shoreline is exactly level with the sea, so zero here means chest deep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); HoldWhileFighting = cfg.Bind<bool>("Safety", "HoldWhileFighting", false, "Hold a follower in place while it is in a fight instead of pulling it out. Off by default, because the wolf you most want rescued is usually the one stuck in a pit WITH something. Turn it on if you would rather your tames finished their fights."); MindHazards = cfg.Bind<bool>("Minding", "MindHazards", true, "Keep tames that are NOT following anybody out of things that would kill them. The game already has this and switches it off for tamed creatures - BaseAI.AvoidFire literally opens by returning early if IsTamed() - so this turns it back on for the animals you parked rather than the ones at heel."); AvoidFire = cfg.Bind<bool>("Minding", "AvoidFire", true, "Open flames. The one everybody loses a boar to."); AvoidLava = cfg.Bind<bool>("Minding", "AvoidLava", true, "Lava. The game has the switch; it is simply off for tames."); AvoidWater = cfg.Bind<bool>("Minding", "AvoidWater", true, "Deep water. Boar and lox do not swim well and drown quietly offscreen."); AvoidSmoke = cfg.Bind<bool>("Minding", "AvoidSmoke", true, "Smoke, which does damage over time. NOTE: this also keeps tames out of a working smokehouse, which is either exactly what you want or exactly what you don't - turn it off if you are curing meat around your animals."); AvoidTraps = cfg.Bind<bool>("Minding", "AvoidTraps", true, "Anything built that would hurt them: sharp stakes, spikes, any trap. Derived from the game rather than a list of names, so modded traps count too - a trap already set to spare friendly targets is left alone, because it was never going to hurt them."); TrapMargin = cfg.Bind<float>("Minding", "TrapMargin", 1.5f, new ConfigDescription("How wide a berth to give a trap, in metres. The game's own fire test uses zero, which stops an animal standing IN something and not against it - and a lox is wider than the point the AI steers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); ForgetAfter = cfg.Bind<float>("Advanced", "ForgetAfter", 300f, new ConfigDescription("Seconds after last seeing a follower before this mod stops considering it yours. This is what lets a follower be recovered after its part of the world has unloaded - which is the entire portal case - without remembering an animal you released an hour ago.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 3600f), Array.Empty<object>())); ServerSide = cfg.Bind<bool>("Server", "ServerSide", true, "On a DEDICATED server, run the catch-up from the server so that players do not have to install Heel at all. It works entirely from ZDOs - which is also why hazard avoidance does not come across: m_avoidFire and friends are fields on a live AI component, and a server never runs one. Ignored everywhere but a dedicated server; a host playing on their own machine gets the client path, which is strictly better because it can see the ground."); Verbose = cfg.Bind<bool>("Advanced", "Verbose", false, "Log every decision. Useful for a bug report, noisy otherwise."); } } } namespace Heel.Commands { internal static class HeelCommand { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void <Register>b__0_0(ConsoleEventArgs args) { args.Context.AddString(Report()); } } internal static void Register() { //IL_0033: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { args.Context.AddString(Report()); }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("heel", "what Heel is tracking and waiting on", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } internal static string Report() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Heel 2.1.0 - " + (HeelConfig.Enabled.Value ? "on" : "OFF")); if (Warden.Running) { foreach (string item in Warden.Report()) { stringBuilder.AppendLine(item); } return stringBuilder.ToString(); } stringBuilder.AppendLine($" adrift beyond {HeelConfig.AdriftDistance.Value:0}m, " + $"with you inside {HeelConfig.WithYouDistance.Value:0}m, " + $"delay {HeelConfig.CatchUpDelay.Value:0}s"); stringBuilder.AppendLine($" following you: {Roster.Count} " + $"clock running: {Recall.Waiting} " + $"brought in: {Recall.Arrived}"); stringBuilder.AppendLine($" moves {Relocate.Moved}, refused {Relocate.Refused}"); stringBuilder.AppendLine($" minding {Minding.Guarded} parked tame(s): " + $"{Minding.Nudged} sent out of harm, " + $"{Minding.Refused} bad spot(s) refused"); if (Roster.Count == 0) { stringBuilder.AppendLine(" Nothing is following you. This mod only ever moves a tame that is tamed, alive, and following YOU - a penned or parked animal is left exactly where it is."); return stringBuilder.ToString(); } foreach (Enrolled item2 in Roster.All) { string value = (item2.Queued ? "queued" : ((item2.WaitingSince > 0f) ? "waiting" : "keeping up")); string text = ((item2.LastDistance >= float.MaxValue) ? "?" : $"{item2.LastDistance:0}m"); stringBuilder.AppendLine(" " + Pad(item2.Name, 22) + " " + Pad(value, 11) + " " + text); } return stringBuilder.ToString(); } internal static string Pad(string value, int width) { value = value ?? string.Empty; if (value.Length < width) { return value + new string(' ', width - value.Length); } return value; } } }