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 Threshold v0.3.10
plugins/Threshold/Threshold.Core.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using ExtraPotions.Threshold.Access; using ExtraPotions.Threshold.Config; using ExtraPotions.Threshold.Conversion; using ExtraPotions.Threshold.Localization; using ExtraPotions.Threshold.Map; using ExtraPotions.Threshold.Picker; using ExtraPotions.Threshold.Portals; using ExtraPotions.Threshold.Routing; using Microsoft.CodeAnalysis; [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("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.3.10.0")] [assembly: AssemblyInformationalVersion("0.3.10+9cc3a345b81f6847a536e593a0f8aa83a8c9e290")] [assembly: AssemblyProduct("Threshold.Core")] [assembly: AssemblyTitle("Threshold.Core")] [assembly: AssemblyVersion("0.3.10.0")] [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 ExtraPotions.Threshold { public static class ThresholdCore { public const string PluginGuid = "com.meadworks.threshold"; public const string ProductName = "Threshold"; public const string ProductVersion = "0.3.10"; public const string Family = "Waystone"; public const string TokenNamespace = "threshold"; public const string TokenPrefix = "meadworks_threshold_"; public const string ZdoPrefix = "threshold."; public const string CharacterDataPrefix = "Threshold."; public const string SupportedGameVersion = "1.0.17"; } } namespace ExtraPotions.Threshold.SeaGates { public readonly struct CrewTeleportDecision { public int LastJump { get; } public bool Decline { get; } public bool ReleaseHelm { get; } public CrewTeleportDecision(int lastJump, bool decline, bool releaseHelm) { LastJump = lastJump; Decline = decline; ReleaseHelm = releaseHelm; } public static CrewTeleportDecision AfterAttempt(int jumpId, bool teleported) { if (!teleported) { return new CrewTeleportDecision(0, decline: true, releaseHelm: false); } return new CrewTeleportDecision(jumpId, decline: false, releaseHelm: true); } } public static class SeaGateAuthority { public static bool AcceptsLanding(bool fromServer, long sender, long senderPlayerId, long reportedPlayerId, long reportedOwner) { if (reportedPlayerId == 0L) { return false; } if (fromServer) { return true; } if (reportedOwner != 0L && sender == reportedOwner) { return true; } if (senderPlayerId != 0L) { return senderPlayerId == reportedPlayerId; } return false; } public static bool AcceptsUnpair(bool fromServer, long sender, long partnerOwner) { if (!fromServer) { if (partnerOwner != 0L) { return sender == partnerOwner; } return false; } return true; } } } namespace ExtraPotions.Threshold.Routing { public enum CartOutcome { None, Bring, StayOff, StayBusy, Refuse } public readonly struct CartFacts { public bool Attached { get; } public bool Enabled { get; } public bool OwnedAndClosed { get; } public bool LoadHardBlocked { get; } public bool LoadTeleportable { get; } public bool EnteredAllowsAllItems { get; } public bool DestinationAllowsAllItems { get; } public OreTransportMode OreTransport { get; } public bool IgnoreRestrictionsCheat { get; } public CartFacts(bool attached, bool enabled, bool ownedAndClosed, bool loadHardBlocked, bool loadTeleportable, bool enteredAllowsAllItems, bool destinationAllowsAllItems, OreTransportMode oreTransport, bool ignoreRestrictionsCheat) { Attached = attached; Enabled = enabled; OwnedAndClosed = ownedAndClosed; LoadHardBlocked = loadHardBlocked; LoadTeleportable = loadTeleportable; EnteredAllowsAllItems = enteredAllowsAllItems; DestinationAllowsAllItems = destinationAllowsAllItems; OreTransport = oreTransport; IgnoreRestrictionsCheat = ignoreRestrictionsCheat; } } public static class CartRules { public const float Ahead = 5f; public const float Lift = 0.3f; public static CartOutcome Decide(CartFacts facts) { if (!facts.Attached) { return CartOutcome.None; } if (!facts.Enabled) { return CartOutcome.StayOff; } if (!facts.OwnedAndClosed) { return CartOutcome.StayBusy; } if (facts.LoadHardBlocked || !CarryRules.Allowed(facts.LoadTeleportable, facts.EnteredAllowsAllItems, facts.DestinationAllowsAllItems, facts.OreTransport, facts.IgnoreRestrictionsCheat)) { return CartOutcome.Refuse; } return CartOutcome.Bring; } public static MapPoint Place(MapPoint anchor, float yaw) { double num = (double)yaw * Math.PI / 180.0; return new MapPoint((float)((double)anchor.X + Math.Sin(num) * 5.0), anchor.Y, (float)((double)anchor.Z + Math.Cos(num) * 5.0)); } public static MapPoint Settle(MapPoint spot, MapPoint traveller, float? floor) { if (floor.HasValue) { float valueOrDefault = floor.GetValueOrDefault(); if (!float.IsNaN(valueOrDefault) && !float.IsInfinity(valueOrDefault) && valueOrDefault <= traveller.Y + 2f && valueOrDefault >= traveller.Y - 4f) { return new MapPoint(spot.X, valueOrDefault + 0.3f, spot.Z); } } return new MapPoint(spot.X, traveller.Y + 0.5f, spot.Z); } } public enum HomeHubSource { None, Home, Unusable } public readonly struct HomeHubPick { public PortalEntry? Hub { get; } public HomeHubSource Source { get; } public HomeHubPick(PortalEntry? hub, HomeHubSource source) { Hub = hub; Source = source; } } public static class HomeHub { public static string DataKey(long worldId) { return "Threshold.homeHub." + worldId.ToString(CultureInfo.InvariantCulture); } public static bool CanSet(PortalEntry portal) { return portal?.IsHub ?? false; } public static string Format(PortalEntry hub) { if (!CanSet(hub)) { throw new ArgumentException("Only a hub can be a home hub.", "hub"); } return PortalKey.Of(hub.Position); } public static string? Parse(string? raw) { string text = raw?.Trim(); if (!PortalKey.IsKey(text)) { return null; } return text; } public static HomeHubPick Resolve(string? homeKey, PortalEntry portal, IEnumerable<PortalEntry> world, AccessViewer viewer, bool accessModesEnabled) { if (portal == null) { throw new ArgumentNullException("portal"); } List<PortalEntry> source = (world ?? Enumerable.Empty<PortalEntry>()).Where((PortalEntry p) => p.IsHub && !p.Id.Equals(portal.Id) && PortalAccessRules.MayTarget(p, viewer, accessModesEnabled)).ToList(); string key = Parse(homeKey); if (key == null) { return new HomeHubPick(null, HomeHubSource.None); } PortalEntry portalEntry = source.FirstOrDefault((PortalEntry p) => PortalKey.Of(p.Position) == key); if (portalEntry == null) { return new HomeHubPick(null, HomeHubSource.Unusable); } return new HomeHubPick(portalEntry, HomeHubSource.Home); } } public static class JumpRules { public const float GameArrival = 8f; private const float MinSeconds = 0.05f; public const float MoveAt = 2f; public const float PreMoveMax = 0.25f; public static float Seconds(float metres, float range, float shortest) { float num = ((range > 0f) ? Clamp01(metres / range) : 1f); float num2 = Math.Min(Math.Max(shortest, 0f), 8f); return num2 + (8f - num2) * num; } public static float Speed(float seconds) { return Speed(seconds, 8f); } public static float Speed(float seconds, float span) { return span / Math.Max(seconds, 0.05f); } public static float MapDistance(MapPoint a, MapPoint b) { double num = a.X - b.X; double num2 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2); } public static float Step(float timer, float dt, float seconds) { if (timer >= 8f || seconds >= 8f || dt <= 0f) { return timer; } float num = Math.Min(0.25f, seconds * 0.25f); if (timer < 2f) { return Math.Min(timer + dt * (Speed(num, 2f) - 1f), 2f); } return Math.Min(timer + dt * (Speed(seconds - num, 6f) - 1f), 8f); } public static float HoldLanding(float timer, float dt, float sinceStart, bool settled) { if (timer < 2f || sinceStart >= 8f || settled) { return timer; } return Math.Min(timer, 8f - dt * 1.5f); } private static float Clamp01(float value) { if (!float.IsNaN(value)) { return Math.Min(1f, Math.Max(0f, value)); } return 1f; } } public enum PortalAction { Use, UseTagEdit, Enter, Pick, ReturnToHub } public enum RouteKind { Vanilla, OpenPicker, TravelToDestination, TravelToHub, Refused } public enum RouteRefusal { None, NoAccess, SamePortal } public sealed class RouteFacts { public PortalAction Action { get; } public bool ProductActive { get; } public PortalEntry Portal { get; } public bool HasVanillaPair { get; } public ReturnToHubMode ReturnToHub { get; } public PortalEntry? Destination { get; } public IReadOnlyList<PortalEntry> World { get; } public AccessViewer Viewer { get; } public bool AccessModesEnabled { get; } public string? HomeHubKey { get; } public RouteFacts(PortalAction action, bool productActive, PortalEntry portal, bool hasVanillaPair, ReturnToHubMode returnToHub, PortalEntry? destination, IReadOnlyList<PortalEntry> world, AccessViewer viewer, bool accessModesEnabled, string? homeHubKey = null) { Action = action; ProductActive = productActive; Portal = portal ?? throw new ArgumentNullException("portal"); HasVanillaPair = hasVanillaPair; ReturnToHub = returnToHub; Destination = destination; World = world ?? Array.Empty<PortalEntry>(); Viewer = viewer; AccessModesEnabled = accessModesEnabled; HomeHubKey = homeHubKey; } } public readonly struct Route { public RouteKind Kind { get; } public PortalEntry? Target { get; } public RouteRefusal Refusal { get; } public static Route Vanilla => new Route(RouteKind.Vanilla, null, RouteRefusal.None); public static Route Picker => new Route(RouteKind.OpenPicker, null, RouteRefusal.None); private Route(RouteKind kind, PortalEntry? target, RouteRefusal refusal) { Kind = kind; Target = target; Refusal = refusal; } public static Route To(PortalEntry target) { return new Route(RouteKind.TravelToDestination, target, RouteRefusal.None); } public static Route ToHub(PortalEntry hub) { return new Route(RouteKind.TravelToHub, hub, RouteRefusal.None); } public static Route Refuse(RouteRefusal why) { return new Route(RouteKind.Refused, null, why); } public override string ToString() { return Kind.ToString() + ((Target != null) ? (" " + Target) : "") + ((Refusal != RouteRefusal.None) ? (" (" + Refusal.ToString() + ")") : ""); } } public static class RouteTable { public static Route Decide(RouteFacts facts) { if (facts == null) { throw new ArgumentNullException("facts"); } if (!facts.ProductActive) { return Route.Vanilla; } PortalEntry portal = facts.Portal; switch (facts.Action) { case PortalAction.Use: case PortalAction.UseTagEdit: return Route.Vanilla; case PortalAction.Pick: if (facts.Destination == null) { return Route.Vanilla; } if (facts.Destination.Id.Equals(portal.Id)) { return Route.Refuse(RouteRefusal.SamePortal); } if (!PortalAccessRules.MayTarget(facts.Destination, facts.Viewer, facts.AccessModesEnabled)) { return Route.Refuse(RouteRefusal.NoAccess); } return Route.To(facts.Destination); case PortalAction.Enter: return Route.Picker; default: { if (portal.IsHub) { return Route.Vanilla; } switch (facts.ReturnToHub) { case ReturnToHubMode.UnpairedOnly: if (facts.HasVanillaPair) { return Route.Vanilla; } break; default: return Route.Vanilla; case ReturnToHubMode.Always: break; } PortalEntry hub = HomeHub.Resolve(facts.HomeHubKey, portal, facts.World, facts.Viewer, facts.AccessModesEnabled).Hub; if (hub != null) { return Route.ToHub(hub); } return Route.Vanilla; } } } } public readonly struct TameCandidate { public int Id { get; } public MapPoint Position { get; } public bool Tamed { get; } public bool FollowsTraveller { get; } public bool Dead { get; } public TameCandidate(int id, MapPoint position, bool tamed, bool followsTraveller, bool dead) { Id = id; Position = position; Tamed = tamed; FollowsTraveller = followsTraveller; Dead = dead; } } public static class TameRules { public const float Spacing = 2.5f; private const int PerArc = 6; private const float HalfArc = 75f; private const float LandedRange = 6f; public const float MaxStepUp = 2f; public const float MaxStepDown = 4f; public static IReadOnlyList<int> Select(IEnumerable<TameCandidate> candidates, MapPoint traveller, float radius, int max) { if (candidates == null) { throw new ArgumentNullException("candidates"); } float r = ThresholdSettings.Tames.TameRadius.Normalize(radius); int count = ThresholdSettings.Tames.MaxTames.Normalize(max); return (from c in (from c in candidates where c.Tamed && c.FollowsTraveller && !c.Dead && c.Position.IsFinite select (Id: c.Id, Distance: Distance(c.Position, traveller)) into c where c.Distance <= (double)r orderby c.Distance, c.Id select c).Take(count) select c.Id).ToList(); } public static MapPoint Place(MapPoint anchor, float yaw, int index, int count) { int num = Math.Max(0, index) / 6; int num2 = Math.Min(6, Math.Max(1, count) - num * 6); int num3 = Math.Max(0, index) % 6; float num4 = ((num2 <= 1) ? 0f : (-75f + 150f * (float)num3 / (float)(num2 - 1))); double num5 = (double)(yaw + num4) * Math.PI / 180.0; float num6 = 2.5f * (float)(num + 1); return new MapPoint((float)((double)anchor.X + Math.Sin(num5) * (double)num6), anchor.Y, (float)((double)anchor.Z + Math.Cos(num5) * (double)num6)); } public static float ProbeHeight(MapPoint traveller) { return traveller.Y + 2f; } public static MapPoint Settle(MapPoint spot, MapPoint traveller, float? floor) { if (floor.HasValue) { float valueOrDefault = floor.GetValueOrDefault(); if (!float.IsNaN(valueOrDefault) && !float.IsInfinity(valueOrDefault) && valueOrDefault <= traveller.Y + 2f && valueOrDefault >= traveller.Y - 4f) { return new MapPoint(spot.X, valueOrDefault, spot.Z); } } return traveller; } public static bool Landed(MapPoint traveller, MapPoint exit) { double num = traveller.X - exit.X; double num2 = traveller.Z - exit.Z; return Math.Sqrt(num * num + num2 * num2) <= 6.0; } private static double Distance(MapPoint a, MapPoint b) { double num = a.X - b.X; double num2 = a.Y - b.Y; double num3 = a.Z - b.Z; return Math.Sqrt(num * num + num2 * num2 + num3 * num3); } } public enum TravelRefusal { None, NoDestination, SamePortal, NoAccess, WorldBlocked, BossBlocked, Carry, Fuel } public sealed class TravelFacts { public Route Route { get; } public bool NoPortalsKey { get; } public bool BossBlocked { get; } public bool InventoryHardBlocked { get; } public bool InventoryTeleportable { get; } public bool EnteredAllowsAllItems { get; } public bool IgnoreRestrictionsCheat { get; } public bool FuelEnabled { get; } public int FuelAmount { get; } public int FuelCarried { get; } public OreTransportMode OreTransport { get; } public TravelFacts(Route route, bool noPortalsKey, bool bossBlocked, bool inventoryHardBlocked, bool inventoryTeleportable, bool enteredAllowsAllItems, bool ignoreRestrictionsCheat, bool fuelEnabled, int fuelAmount, int fuelCarried) : this(route, noPortalsKey, bossBlocked, inventoryHardBlocked, inventoryTeleportable, enteredAllowsAllItems, ignoreRestrictionsCheat, fuelEnabled, fuelAmount, fuelCarried, OreTransportMode.FromStoneToWood) { } public TravelFacts(Route route, bool noPortalsKey, bool bossBlocked, bool inventoryHardBlocked, bool inventoryTeleportable, bool enteredAllowsAllItems, bool ignoreRestrictionsCheat, bool fuelEnabled, int fuelAmount, int fuelCarried, OreTransportMode oreTransport) { Route = route; NoPortalsKey = noPortalsKey; BossBlocked = bossBlocked; InventoryHardBlocked = inventoryHardBlocked; InventoryTeleportable = inventoryTeleportable; EnteredAllowsAllItems = enteredAllowsAllItems; IgnoreRestrictionsCheat = ignoreRestrictionsCheat; FuelEnabled = fuelEnabled; FuelAmount = fuelAmount; FuelCarried = fuelCarried; OreTransport = oreTransport; } } public readonly struct TravelPlan { public TravelRefusal Refusal { get; } public PortalEntry? Target { get; } public FuelCheck Fuel { get; } public bool Go { get { if (Refusal == TravelRefusal.None) { return Target != null; } return false; } } public TravelPlan(TravelRefusal refusal, PortalEntry? target, FuelCheck fuel) { Refusal = refusal; Target = target; Fuel = fuel; } } public static class TravelRules { public static PortalEntry? CurrentTarget(PortalEntry selected, IEnumerable<PortalEntry> current) { return current.FirstOrDefault((PortalEntry portal) => portal.Id.Equals(selected.Id)); } public static TravelPlan Decide(TravelFacts facts) { if (facts == null) { throw new ArgumentNullException("facts"); } FuelCheck fuel = new FuelCheck(FuelOutcome.NotNeeded, 0, 0); Route route = facts.Route; switch (route.Kind) { case RouteKind.Refused: return new TravelPlan((route.Refusal == RouteRefusal.SamePortal) ? TravelRefusal.SamePortal : TravelRefusal.NoAccess, null, fuel); case RouteKind.TravelToDestination: case RouteKind.TravelToHub: { if (route.Target == null) { return new TravelPlan(TravelRefusal.NoDestination, null, fuel); } if (facts.NoPortalsKey) { return new TravelPlan(TravelRefusal.WorldBlocked, null, fuel); } if (facts.BossBlocked) { return new TravelPlan(TravelRefusal.BossBlocked, null, fuel); } if (facts.InventoryHardBlocked || !CarryRules.Allowed(facts.InventoryTeleportable, facts.EnteredAllowsAllItems, route.Target.AllowsAllItems, facts.OreTransport, facts.IgnoreRestrictionsCheat)) { return new TravelPlan(TravelRefusal.Carry, null, fuel); } FuelCheck fuel2 = FuelRules.Check(facts.FuelEnabled, facts.FuelAmount, facts.FuelCarried, facts.IgnoreRestrictionsCheat); if (fuel2.Outcome == FuelOutcome.NotEnough) { return new TravelPlan(TravelRefusal.Fuel, null, fuel2); } return new TravelPlan(TravelRefusal.None, route.Target, fuel2); } default: return new TravelPlan(TravelRefusal.NoDestination, null, fuel); } } public static bool BossBlocked(bool noBossPortalsKey, bool bossEvent, float activeBosses) { if (noBossPortalsKey) { if (!bossEvent) { return activeBosses > 0f; } return true; } return false; } public static MapPoint ExitPoint(MapPoint destination, float yaw, float exitDistance) { double num = (double)yaw * Math.PI / 180.0; return new MapPoint((float)((double)destination.X + Math.Sin(num) * (double)exitDistance), destination.Y + 1f, (float)((double)destination.Z + Math.Cos(num) * (double)exitDistance)); } public static string TextKey(TravelRefusal refusal) { return refusal switch { TravelRefusal.NoAccess => ThresholdTextKeys.Refusal(RouteRefusal.NoAccess), TravelRefusal.SamePortal => ThresholdTextKeys.Refusal(RouteRefusal.SamePortal), TravelRefusal.WorldBlocked => "$msg_blocked", TravelRefusal.BossBlocked => "$msg_blockedbyboss", TravelRefusal.Carry => "carry_blocked", TravelRefusal.Fuel => "fuel_not_enough", _ => "", }; } } public static class CarryRules { public static bool Allowed(bool inventoryTeleportable, bool enteredAllowsAllItems, bool ignoreRestrictionsCheat) { return Allowed(inventoryTeleportable, enteredAllowsAllItems, destinationAllowsAllItems: false, OreTransportMode.FromStoneToWood, ignoreRestrictionsCheat); } public static bool Allowed(bool inventoryTeleportable, bool enteredAllowsAllItems, bool destinationAllowsAllItems, OreTransportMode policy, bool ignoreRestrictionsCheat) { if (inventoryTeleportable || ignoreRestrictionsCheat) { return true; } return ThresholdSettings.General.OreTransport.Normalize(policy) switch { OreTransportMode.Blocked => false, OreTransportMode.StoneToStone => enteredAllowsAllItems && destinationAllowsAllItems, OreTransportMode.All => true, _ => enteredAllowsAllItems, }; } public static bool Teleportable(bool gameTeleportable, IEnumerable<string> restrictedCarried, bool listCheat, string listedItems) { if (gameTeleportable) { return true; } if (!listCheat || restrictedCarried == null) { return false; } List<string> list = restrictedCarried.ToList(); if (list.Count > 0) { return Unlisted(list, listedItems).Count == 0; } return false; } public static IReadOnlyList<string> Unlisted(IEnumerable<string> restrictedCarried, string listedItems) { HashSet<string> hashSet = new HashSet<string>(ThresholdSettings.NameList(listedItems ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); List<string> list = new List<string>(); foreach (string item2 in restrictedCarried ?? Enumerable.Empty<string>()) { string item = (item2 ?? "").Trim(); if (!hashSet.Contains(item) && hashSet2.Add(item)) { list.Add(item); } } return list; } } public enum FuelOutcome { NotNeeded, Consume, NotEnough } public readonly struct FuelCheck { public FuelOutcome Outcome { get; } public int Amount { get; } public int Missing { get; } public FuelCheck(FuelOutcome outcome, int amount, int missing) { Outcome = outcome; Amount = amount; Missing = missing; } } public static class FuelRules { public static FuelCheck Check(bool enabled, int amount, int carried, bool ignoreRestrictionsCheat) { if (!enabled || ignoreRestrictionsCheat) { return new FuelCheck(FuelOutcome.NotNeeded, 0, 0); } int num = ThresholdSettings.Fuel.FuelAmount.Normalize(amount); int num2 = Math.Max(0, carried); if (num2 < num) { return new FuelCheck(FuelOutcome.NotEnough, num, num - num2); } return new FuelCheck(FuelOutcome.Consume, num, 0); } } } namespace ExtraPotions.Threshold.Portals { public struct ListHash { private ulong value; public long Value { get { if (value == 0L) { return 1L; } return (long)value; } } public static ListHash Start(int count) { ListHash result = new ListHash { value = 11400714819323198485uL }; result.Add(count); return result; } public void Add(long part) { value = Mix(value ^ (ulong)part); } public void Add(int part) { Add((long)part); } public void Add(uint part) { Add((long)part); } public void Add(float part) { Add(BitConverter.DoubleToInt64Bits(part)); } public void Add(float x, float y, float z) { Add(x); Add(y); Add(z); } public void Add(MapPoint part) { Add(part.X, part.Y, part.Z); } private static ulong Mix(ulong x) { x = (x ^ (x >> 30)) * 13787848793156543929uL; x = (x ^ (x >> 27)) * 10723151780598845931uL; return x ^ (x >> 31); } } public sealed class PortalAskSchedule { public const double OpenSeconds = 5.0; public const double IdleSeconds = 60.0; private double last = double.NegativeInfinity; private bool now = true; public void Request() { now = true; } public bool Due(double time, bool open) { if (!now) { return time >= last + (open ? 5.0 : 60.0); } return true; } public void Sent(double time) { now = false; last = time; } public void Reset() { now = true; last = double.NegativeInfinity; } } public readonly struct MapPoint : IEquatable<MapPoint> { public float X { get; } public float Y { get; } public float Z { get; } public bool IsFinite { get { if (!float.IsNaN(X) && !float.IsInfinity(X) && !float.IsNaN(Y) && !float.IsInfinity(Y) && !float.IsNaN(Z)) { return !float.IsInfinity(Z); } return false; } } public MapPoint(float x, float y, float z) { X = x; Y = y; Z = z; } public double FlatDistance(MapPoint other) { double num = X - other.X; double num2 = Z - other.Z; return Math.Sqrt(num * num + num2 * num2); } public bool Equals(MapPoint other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Z.Equals(other.Z); } return false; } public override bool Equals(object? obj) { if (obj is MapPoint other) { return Equals(other); } return false; } public override int GetHashCode() { return (X, Y, Z).GetHashCode(); } public override string ToString() { return "(" + X + ", " + Y + ", " + Z + ")"; } } public readonly struct PortalId : IEquatable<PortalId> { public long User { get; } public uint Id { get; } public PortalId(long user, uint id) { User = user; Id = id; } public bool Equals(PortalId other) { if (User == other.User) { return Id == other.Id; } return false; } public override bool Equals(object? obj) { if (obj is PortalId other) { return Equals(other); } return false; } public override int GetHashCode() { return (User, Id).GetHashCode(); } public override string ToString() { return User + ":" + Id; } } public sealed class PortalEntry { public PortalId Id { get; } public string Prefab { get; } public string Tag { get; } public MapPoint Position { get; } public AccessMode Mode { get; } public long Owner { get; } public string HubName { get; } public bool AllowsAllItems { get; } public float Yaw { get; } public bool IsHub => HubName.Length > 0; public bool IsTagged => Tag.Trim().Length > 0; public PortalEntry(PortalId id, string prefab, string tag, MapPoint position, AccessMode mode, long owner, string hubName, bool allowsAllItems, float yaw = 0f) { if (string.IsNullOrWhiteSpace(prefab)) { throw new ArgumentException("Expected a prefab name.", "prefab"); } if (!position.IsFinite) { throw new ArgumentException("Expected a finite position.", "position"); } Id = id; Prefab = prefab; Tag = tag ?? ""; Position = position; Mode = (Enum.IsDefined(typeof(AccessMode), mode) ? mode : AccessMode.Public); Owner = owner; HubName = (hubName ?? "").Trim(); AllowsAllItems = allowsAllItems; Yaw = ((float.IsNaN(yaw) || float.IsInfinity(yaw)) ? 0f : yaw); } public PortalEntry WithTag(string tag) { return new PortalEntry(Id, Prefab, tag, Position, Mode, Owner, HubName, AllowsAllItems, Yaw); } public PortalEntry WithMode(AccessMode mode) { return new PortalEntry(Id, Prefab, Tag, Position, mode, Owner, HubName, AllowsAllItems, Yaw); } public PortalEntry WithHub(string hubName) { return new PortalEntry(Id, Prefab, Tag, Position, Mode, Owner, hubName, AllowsAllItems, Yaw); } public override string ToString() { return Prefab + " '" + Tag + "' " + Position.ToString() + (IsHub ? (" hub '" + HubName + "'") : ""); } } public static class PortalKey { public static string Of(MapPoint position) { if (!position.IsFinite) { throw new ArgumentException("Expected a finite position.", "position"); } return Round(position.X) + "/" + Round(position.Y) + "/" + Round(position.Z); } public static IReadOnlyList<string> ParseSet(string? raw) { List<string> list = new List<string>(); if (string.IsNullOrEmpty(raw)) { return list; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = raw.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (IsKey(text) && hashSet.Add(text)) { list.Add(text); } } return list; } public static string FormatSet(IEnumerable<string> keys) { return string.Join(",", (keys ?? Enumerable.Empty<string>()).Where(IsKey).Distinct<string>(StringComparer.Ordinal)); } public static bool IsKey(string? entry) { if (string.IsNullOrEmpty(entry)) { return false; } string[] array = entry.Split('/'); int result; if (array.Length == 3) { return array.All((string b) => int.TryParse(b, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out result)); } return false; } private static string Round(float value) { return ((int)Math.Round(value, MidpointRounding.ToEven)).ToString(CultureInfo.InvariantCulture); } } public static class PortalListVersion { public static long Of(IReadOnlyList<PortalEntry> portals) { if (portals == null) { throw new ArgumentNullException("portals"); } ListHash hash = ListHash.Start(portals.Count); foreach (PortalEntry portal in portals) { hash.Add(portal.Id.User); hash.Add(portal.Id.Id); Add(ref hash, portal.Prefab); Add(ref hash, portal.Tag); hash.Add(portal.Position); hash.Add((int)portal.Mode); hash.Add(portal.Owner); Add(ref hash, portal.HubName); hash.Add(portal.AllowsAllItems ? 1 : 0); hash.Add(portal.Yaw); } return hash.Value; } private static void Add(ref ListHash hash, string text) { hash.Add(text.Length); foreach (char part in text) { hash.Add(part); } } } public static class PortalFields { public const string ModeKey = "threshold.mode"; public const string OwnerKey = "threshold.owner"; public const string HubKey = "threshold.hub"; public const int NoMode = -1; public static AccessMode Mode(int raw) { if (raw < 0 || !Enum.IsDefined(typeof(AccessMode), raw)) { return AccessMode.Public; } return (AccessMode)raw; } } public readonly struct PortalListRequest { public long Held { get; } public int Page { get; } public PortalListRequest(long held, int page) { Held = held; Page = page; } } public sealed class PortalListPage { public long Version { get; } public int Page { get; } public int Pages { get; } public IReadOnlyList<PortalEntry> Entries { get; } public bool IsUnchanged { get; } public PortalListPage(long version, int page, int pages, IReadOnlyList<PortalEntry> entries) { if (pages < 1 || page < 0 || page >= pages) { throw new ArgumentOutOfRangeException("page", "Page outside the list."); } Version = version; Page = page; Pages = pages; Entries = entries ?? throw new ArgumentNullException("entries"); } private PortalListPage(long version) { Version = version; Entries = Array.Empty<PortalEntry>(); IsUnchanged = true; } public static PortalListPage Unchanged(long version) { return new PortalListPage(version); } } public static class PortalListCodec { public const int PageBudget = 57344; public const int MaxEntriesPerPage = 4096; public const int MaxPages = 256; public const int MaxStringBytes = 256; private const byte RequestFormat = 1; private const byte PageFormat = 1; private const byte UnchangedFormat = 2; private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); public const int PageHeaderSize = 21; public static byte[] EncodeRequest(PortalListRequest request) { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)1); binaryWriter.Write(request.Held); binaryWriter.Write(request.Page); binaryWriter.Flush(); return memoryStream.ToArray(); } public static PortalListRequest DecodeRequest(byte[] bytes) { return Read(bytes, delegate(BinaryReader reader) { if (reader.ReadByte() != 1) { throw new FormatException("Unknown request format."); } long held = reader.ReadInt64(); int num = reader.ReadInt32(); if (num < 0 || num >= 256) { throw new FormatException("Page out of range."); } return new PortalListRequest(held, num); }); } public static byte[] EncodeReply(PortalListPage page) { if (page == null) { throw new ArgumentNullException("page"); } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); if (page.IsUnchanged) { binaryWriter.Write((byte)2); binaryWriter.Write(page.Version); } else { binaryWriter.Write((byte)1); binaryWriter.Write(page.Version); binaryWriter.Write(page.Page); binaryWriter.Write(page.Pages); binaryWriter.Write(page.Entries.Count); foreach (PortalEntry entry in page.Entries) { WriteEntry(binaryWriter, entry); } } binaryWriter.Flush(); return memoryStream.ToArray(); } public static PortalListPage DecodeReply(byte[] bytes) { return Read(bytes, delegate(BinaryReader reader) { switch (reader.ReadByte()) { case 2: return PortalListPage.Unchanged(reader.ReadInt64()); default: throw new FormatException("Unknown page format."); case 1: { long version = reader.ReadInt64(); int num = reader.ReadInt32(); int num2 = reader.ReadInt32(); if (num2 < 1 || num2 > 256 || num < 0 || num >= num2) { throw new FormatException("Page out of range."); } int num3 = reader.ReadInt32(); if (num3 < 0 || num3 > 4096) { throw new FormatException("Entry count out of range."); } List<PortalEntry> list = new List<PortalEntry>(num3); for (int i = 0; i < num3; i++) { list.Add(ReadEntry(reader)); } return new PortalListPage(version, num, num2, list); } } }); } public static int EntrySize(PortalEntry entry) { using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); WriteEntry(binaryWriter, entry); binaryWriter.Flush(); return (int)memoryStream.Length; } private static void WriteEntry(BinaryWriter writer, PortalEntry entry) { writer.Write(entry.Id.User); writer.Write(entry.Id.Id); WriteString(writer, entry.Prefab); WriteString(writer, entry.Tag); writer.Write(entry.Position.X); writer.Write(entry.Position.Y); writer.Write(entry.Position.Z); writer.Write((byte)entry.Mode); writer.Write(entry.Owner); WriteString(writer, entry.HubName); writer.Write(entry.AllowsAllItems); writer.Write(entry.Yaw); } private static PortalEntry ReadEntry(BinaryReader reader) { long user = reader.ReadInt64(); uint id = reader.ReadUInt32(); string text = ReadString(reader); string tag = ReadString(reader); MapPoint position = new MapPoint(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); AccessMode mode = PortalFields.Mode(reader.ReadByte()); long owner = reader.ReadInt64(); string hubName = ReadString(reader); byte b = reader.ReadByte(); float yaw = reader.ReadSingle(); if (b > 1) { throw new FormatException("Malformed flag."); } if (text.Length == 0 || !position.IsFinite) { throw new FormatException("Malformed portal."); } return new PortalEntry(new PortalId(user, id), text, tag, position, mode, owner, hubName, b == 1, yaw); } private static void WriteString(BinaryWriter writer, string text) { string text2 = text ?? ""; byte[] bytes = Utf8.GetBytes(text2); while (bytes.Length > 256) { text2 = text2.Substring(0, text2.Length - 1); if (text2.Length > 0 && char.IsHighSurrogate(text2[text2.Length - 1])) { text2 = text2.Substring(0, text2.Length - 1); } bytes = Utf8.GetBytes(text2); } writer.Write((ushort)bytes.Length); writer.Write(bytes); } private static string ReadString(BinaryReader reader) { int num = reader.ReadUInt16(); if (num > 256) { throw new FormatException("String too long."); } byte[] array = reader.ReadBytes(num); if (array.Length != num) { throw new FormatException("Truncated string."); } try { return Utf8.GetString(array); } catch (ArgumentException innerException) { throw new FormatException("Malformed text.", innerException); } } private static T Read<T>(byte[] bytes, Func<BinaryReader, T> read) { if (bytes == null) { throw new FormatException("No bytes."); } try { using MemoryStream memoryStream = new MemoryStream(bytes, writable: false); using BinaryReader arg = new BinaryReader(memoryStream); T result = read(arg); if (memoryStream.Position != memoryStream.Length) { throw new FormatException("Trailing bytes."); } return result; } catch (EndOfStreamException innerException) { throw new FormatException("Truncated.", innerException); } catch (ArgumentOutOfRangeException innerException2) { throw new FormatException("Out of range.", innerException2); } catch (ArgumentException innerException3) { throw new FormatException("Malformed.", innerException3); } } } public static class PortalListPager { public static IReadOnlyList<IReadOnlyList<PortalEntry>> Split(IReadOnlyList<PortalEntry> portals, int budget) { if (portals == null) { throw new ArgumentNullException("portals"); } List<IReadOnlyList<PortalEntry>> list = new List<IReadOnlyList<PortalEntry>>(); List<PortalEntry> list2 = new List<PortalEntry>(); int num = 21; foreach (PortalEntry portal in portals) { int num2 = PortalListCodec.EntrySize(portal); if (21 + num2 > budget) { throw new ArgumentException("One portal exceeds the page budget.", "budget"); } if (list2.Count > 0 && (num + num2 > budget || list2.Count >= 4096)) { list.Add(list2); list2 = new List<PortalEntry>(); num = 21; } list2.Add(portal); num += num2; } list.Add(list2); if (list.Count > 256) { throw new InvalidOperationException("The portal list exceeds " + 256 + " pages."); } return list; } } public sealed class PortalListAssembly { private readonly List<PortalEntry> collecting = new List<PortalEntry>(); private long collectingVersion; private int nextPage; private IReadOnlyList<PortalEntry> portals = Array.Empty<PortalEntry>(); public long Held { get; private set; } public IReadOnlyList<PortalEntry> Portals => portals; public int Generation { get; private set; } public PortalListRequest Next() { return new PortalListRequest(Held, nextPage); } public bool Accept(PortalListPage page) { if (page == null) { throw new ArgumentNullException("page"); } if (page.IsUnchanged) { if (page.Version != Held) { Restart(); } return false; } if (page.Page == 0) { collecting.Clear(); collectingVersion = page.Version; nextPage = 0; } else if (page.Version != collectingVersion || page.Page != nextPage) { if (page.Version != collectingVersion) { Restart(); } return false; } collecting.AddRange(page.Entries); nextPage = page.Page + 1; if (nextPage < page.Pages) { return false; } portals = collecting.ToArray(); Held = page.Version; Generation++; Restart(); return true; } public void Reset() { Restart(); portals = Array.Empty<PortalEntry>(); Held = 0L; Generation++; } private void Restart() { collecting.Clear(); collectingVersion = 0L; nextPage = 0; } } public sealed class PrefabFilter { public static readonly IReadOnlyList<string> PortalSelectorDefault = Array.AsReadOnly(new string[3] { "portal_wood", "portal_stone", "portal" }); private readonly HashSet<string>? names; public bool IncludesEveryPortal => names == null; public IReadOnlyCollection<string> Names { get { IReadOnlyCollection<string> readOnlyCollection = names; return (IReadOnlyCollection<string>)(((object)readOnlyCollection) ?? ((object)Array.Empty<string>())); } } private PrefabFilter(HashSet<string>? names) { this.names = names; } public static PrefabFilter Parse(string? value) { string text = ThresholdSettings.NameList(value ?? ""); return new PrefabFilter((text.Length == 0) ? null : new HashSet<string>(text.Split(','), StringComparer.Ordinal)); } public bool Includes(string prefab) { if (!string.IsNullOrEmpty(prefab)) { if (names != null) { return names.Contains(prefab); } return true; } return false; } public static bool IsPortalSelectorDefault(string? value) { PrefabFilter prefabFilter = Parse(value); if (!prefabFilter.IncludesEveryPortal && prefabFilter.Names.Count == PortalSelectorDefault.Count) { return PortalSelectorDefault.All(prefabFilter.Includes); } return false; } } } namespace ExtraPotions.Threshold.Picker { public sealed class DeferredOpen<T> where T : class { private T? pending; public bool Pending => pending != null; public void Request(T value) { pending = value; } public T? Take() { T? result = pending; pending = null; return result; } public void Clear() { pending = null; } } public sealed class FavouriteSet { public const string CharacterDataKey = "Threshold.favorites"; private readonly List<string> order = new List<string>(); private readonly HashSet<string> keys = new HashSet<string>(StringComparer.Ordinal); public int Count => order.Count; public int Version { get; private set; } public FavouriteSet(IEnumerable<string> keys) { foreach (string item in keys ?? Array.Empty<string>()) { if (PortalKey.IsKey(item) && this.keys.Add(item)) { order.Add(item); } } } public static FavouriteSet Parse(string? raw) { return new FavouriteSet(PortalKey.ParseSet(raw)); } public bool Contains(string key) { if (key != null) { return keys.Contains(key); } return false; } public bool Toggle(string key) { if (!PortalKey.IsKey(key)) { throw new ArgumentException("Expected a portal position key.", "key"); } Version++; if (keys.Remove(key)) { order.Remove(key); return false; } keys.Add(key); order.Add(key); return true; } public string Format() { return PortalKey.FormatSet(order); } } public sealed class PickerFailures { public const int Limit = 3; private int inRow; public bool Disabled => inRow >= 3; public bool Failed() { if (Disabled) { return false; } inRow++; return Disabled; } public void Opened() { inRow = 0; } public void Reset() { inRow = 0; } } public enum PickerCategory { Favorites, Hubs, Stone, Private, Public, Admin } public sealed class PickerRow { public PortalEntry Portal { get; } public PickerCategory Category { get; } public string Label { get; } public double Distance { get; } public bool IsFavourite { get; } internal PickerRow(PortalEntry portal, PickerCategory category, string label, double distance, bool favourite) { Portal = portal; Category = category; Label = label; Distance = distance; IsFavourite = favourite; } } public sealed class PickerGroup { public PickerCategory Category { get; } public IReadOnlyList<PickerRow> Rows { get; } internal PickerGroup(PickerCategory category, IReadOnlyList<PickerRow> rows) { Category = category; Rows = rows; } } public sealed class PickerQuery { public IReadOnlyList<PortalEntry> World { get; } public PortalEntry? Current { get; } public MapPoint Player { get; } public AccessViewer Viewer { get; } public bool AccessModesEnabled { get; } public string Search { get; } public SortOrder Sort { get; } public FavouriteSet Favourites { get; } public bool ListUntagged { get; } public Func<MapPoint, bool>? Explored { get; } public PrefabFilter Prefabs { get; } public bool StoneGroup { get; } public PickerQuery(IReadOnlyList<PortalEntry> world, PortalEntry? current, MapPoint player, AccessViewer viewer, bool accessModesEnabled, string search, SortOrder sort, FavouriteSet favourites, bool listUntagged, Func<MapPoint, bool>? explored, PrefabFilter prefabs, bool stoneGroup = false) { World = world ?? Array.Empty<PortalEntry>(); Current = current; Player = player; Viewer = viewer; AccessModesEnabled = accessModesEnabled; Search = search ?? ""; Sort = sort; Favourites = favourites ?? new FavouriteSet(Array.Empty<string>()); ListUntagged = listUntagged; Explored = explored; Prefabs = prefabs ?? PrefabFilter.Parse(""); StoneGroup = stoneGroup; } } public static class PickerList { public static PickerCategory CategoryOf(PortalEntry portal, bool accessModesEnabled, bool stoneGroup = false) { if (portal.IsHub) { return PickerCategory.Hubs; } if (stoneGroup && portal.AllowsAllItems) { return PickerCategory.Stone; } return PortalAccessRules.EffectiveMode(portal.Mode, accessModesEnabled) switch { AccessMode.Private => PickerCategory.Private, AccessMode.Admin => PickerCategory.Admin, _ => PickerCategory.Public, }; } public static string LabelOf(PortalEntry portal) { if (!portal.IsHub) { return portal.Tag; } return portal.HubName; } public static IReadOnlyList<PickerGroup> Build(PickerQuery query) { if (query == null) { throw new ArgumentNullException("query"); } string[] array = TextFold.Fold(query.Search).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); List<PickerRow> list = new List<PickerRow>(); foreach (PortalEntry item in query.World) { if ((query.Current != null && item.Id.Equals(query.Current.Id)) || !query.Prefabs.Includes(item.Prefab) || (!item.IsHub && !item.IsTagged && !query.ListUntagged) || !PortalAccessRules.MayTarget(item, query.Viewer, query.AccessModesEnabled) || (query.Explored != null && !query.Explored(item.Position))) { continue; } string text = LabelOf(item); if (array.Length != 0) { string folded = TextFold.Fold(text); if (!array.All((string w) => folded.Contains(w))) { continue; } } list.Add(new PickerRow(item, CategoryOf(item, query.AccessModesEnabled, query.StoneGroup), text, item.Position.FlatDistance(query.Player), query.Favourites.Contains(PortalKey.Of(item.Position)))); } List<PickerGroup> list2 = new List<PickerGroup>(); foreach (PickerCategory category in Enum.GetValues(typeof(PickerCategory))) { List<PickerRow> list3 = Order((category == PickerCategory.Favorites) ? list.Where((PickerRow r) => r.IsFavourite) : list.Where((PickerRow r) => !r.IsFavourite && r.Category == category), query.Sort).ToList(); if (list3.Count > 0) { list2.Add(new PickerGroup(category, list3)); } } return list2; } private static IEnumerable<PickerRow> Order(IEnumerable<PickerRow> rows, SortOrder sort) { if (sort != SortOrder.Distance) { return rows.OrderBy<PickerRow, string>((PickerRow r) => TextFold.Fold(r.Label), StringComparer.Ordinal).ThenBy((PickerRow r) => r.Distance); } return rows.OrderBy((PickerRow r) => r.Distance).ThenBy<PickerRow, string>((PickerRow r) => TextFold.Fold(r.Label), StringComparer.Ordinal); } } public static class TextFold { public static string Fold(string? text) { if (string.IsNullOrEmpty(text)) { return ""; } string text2 = text.Normalize(NormalizationForm.FormD); StringBuilder stringBuilder = new StringBuilder(text2.Length); bool flag = false; string text3 = text2; foreach (char c in text3) { if (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.NonSpacingMark) { continue; } if (char.IsWhiteSpace(c)) { flag = stringBuilder.Length > 0; continue; } if (flag) { stringBuilder.Append(' '); flag = false; } if (c == 'ß') { stringBuilder.Append("ss"); } else { stringBuilder.Append(char.ToLowerInvariant(c)); } } return stringBuilder.ToString(); } } public static class PickerPadList { public const int Missing = 0; public const int Blocked = 1; public const int Ready = 2; public static bool Unchanged(IReadOnlyList<int> cached, IReadOnlyList<int> now) { if (cached == null || now == null || cached.Count != now.Count) { return false; } for (int i = 0; i < now.Count; i++) { if (cached[i] != now[i]) { return false; } } return true; } } public enum PickerLineKind { Header, Portal } public enum SourceHubAction { None, SetHub, ClearHub } public sealed class PickerLine { public PickerLineKind Kind { get; } public PickerCategory Category { get; } public PickerRow? Row { get; } public int Pin { get; } public bool IsHome { get; } internal PickerLine(PickerLineKind kind, PickerCategory category, PickerRow? row, int pin, bool home) { Kind = kind; Category = category; Row = row; Pin = pin; IsHome = home; } } public sealed class PickerView { private readonly int[] lineOfPin; public IReadOnlyList<PickerLine> Lines { get; } public MapPinPlan Plan { get; } public SourceHubAction SourceHub { get; } public PortalEntry? ReturnHub => Plan.ReturnHub; public bool IsEmpty => Plan.DestinationCount == 0; private PickerView(IReadOnlyList<PickerLine> lines, MapPinPlan plan, SourceHubAction sourceHub, int[] lineOfPin) { Lines = lines; Plan = plan; SourceHub = sourceHub; this.lineOfPin = lineOfPin; } public static PickerView Build(IReadOnlyList<PickerGroup> groups, PortalEntry? current, PortalEntry? returnHub, string? homeHubKey, bool accessModesEnabled = true, bool highlightStone = false) { if (groups == null) { throw new ArgumentNullException("groups"); } MapPinPlan mapPinPlan = MapPinPlan.Build(groups, current, returnHub, homeHubKey, accessModesEnabled, highlightStone); List<PickerLine> list = new List<PickerLine>(); int[] array = new int[mapPinPlan.Pins.Count]; for (int i = 0; i < array.Length; i++) { array[i] = -1; } int num = 0; foreach (PickerGroup group in groups) { if (group.Rows.Count == 0) { continue; } list.Add(new PickerLine(PickerLineKind.Header, group.Category, null, -1, home: false)); foreach (PickerRow row in group.Rows) { array[num] = list.Count; list.Add(new PickerLine(PickerLineKind.Portal, group.Category, row, num, mapPinPlan.Pins[num].IsHome)); num++; } } SourceHubAction sourceHub = ((current != null) ? (current.IsHub ? SourceHubAction.ClearHub : ((!current.IsTagged) ? SourceHubAction.SetHub : SourceHubAction.None)) : SourceHubAction.None); return new PickerView(list.AsReadOnly(), mapPinPlan, sourceHub, array); } public int LineOfPin(int pin) { if (pin < 0 || pin >= lineOfPin.Length) { return -1; } return lineOfPin[pin]; } public PortalEntry? Destination(int line) { if (line < 0 || line >= Lines.Count) { return null; } return Plan.DestinationAt(Lines[line].Pin); } public string? HomeActionKey(int line) { if (line < 0 || line >= Lines.Count || Plan.HomeCandidateAt(Lines[line].Pin) == null) { return null; } if (!Lines[line].IsHome) { return "picker_set_home"; } return "picker_clear_home"; } public static string? TextKey(SourceHubAction action) { return action switch { SourceHubAction.ClearHub => "picker_clear_hub", SourceHubAction.SetHub => "picker_set_hub", _ => null, }; } } public static class PortalModeRules { public static bool Routes(TeleportMode mode) { if (mode != TeleportMode.Map) { return mode == TeleportMode.TargetTeleport; } return true; } public static bool CanEnter(TeleportMode mode, PortalEntry portal) { if (Routes(mode)) { if (mode == TeleportMode.TargetTeleport) { return portal.IsTagged; } return true; } return false; } public static IReadOnlyList<PickerRow> NamedChoices(PickerQuery query) { return (from row in PickerList.Build(query).SelectMany((PickerGroup @group) => @group.Rows) where row.Portal.IsTagged orderby row.IsFavourite descending select row).ThenBy<PickerRow, string>((PickerRow row) => row.Portal.Tag.Trim(), StringComparer.CurrentCultureIgnoreCase).ThenBy((PickerRow row) => row.Distance).ToArray(); } } public static class PortalTriggerRules { public const float ExitMargin = 1f; public static bool Left(float openGap, float gap) { if (float.IsNaN(gap)) { return false; } float num = ((float.IsNaN(openGap) || openGap < 0f || float.IsInfinity(openGap)) ? 0f : openGap); return gap > num + 1f; } } } namespace ExtraPotions.Threshold.Map { public readonly struct PinColour : IEquatable<PinColour> { public float R { get; } public float G { get; } public float B { get; } public float A { get; } public static PinColour White { get; } = new PinColour(1f, 1f, 1f); public PinColour(float r, float g, float b, float a = 1f) { R = r; G = g; B = b; A = a; } public PinColour WithAlpha(float alpha) { return new PinColour(R, G, B, alpha); } public bool Equals(PinColour other) { if (R.Equals(other.R) && G.Equals(other.G) && B.Equals(other.B)) { return A.Equals(other.A); } return false; } public override bool Equals(object? obj) { if (obj is PinColour other) { return Equals(other); } return false; } public override int GetHashCode() { return (R, G, B, A).GetHashCode(); } public override string ToString() { return "(" + R + ", " + G + ", " + B + ", " + A + ")"; } } public static class MapPinColours { public const float HereAlpha = 0.6f; public static PinColour Public { get; } = new PinColour(0.95f, 0.45f, 0.95f); public static PinColour Private { get; } = new PinColour(0.45f, 0.65f, 1f); public static PinColour Admin { get; } = new PinColour(1f, 0.55f, 0.2f); public static PinColour Stone { get; } = new PinColour(0.72f, 0.72f, 0.72f); public static PinColour ReturnHub { get; } = new PinColour(1f, 0.85f, 0.4f); public static PinColour Of(AccessMode mode) { return mode switch { AccessMode.Private => Private, AccessMode.Admin => Admin, _ => Public, }; } public static PinColour Tint(MapPinSpec pin) { if (pin == null) { throw new ArgumentNullException("pin"); } if (!pin.IsStone) { return Of(pin.Mode); } return Stone; } public static PinColour? Header(PickerCategory category) { return category switch { PickerCategory.Public => Public, PickerCategory.Private => Private, PickerCategory.Admin => Admin, PickerCategory.Stone => Stone, _ => null, }; } public static PinColour ForPin(MapPinSpec pin, bool returnHub) { if (pin == null) { throw new ArgumentNullException("pin"); } if (pin.Kind == MapPinKind.Player) { return PinColour.White; } if (returnHub) { return ReturnHub; } PinColour result = Tint(pin); if (pin.Kind != MapPinKind.Here) { return result; } return result.WithAlpha(0.6f); } } public enum MapPinKind { Portal, Hub, Here, Player } public sealed class MapPinSpec { public MapPinKind Kind { get; } public PortalEntry? Portal { get; } public string Label { get; } public bool IsFavourite { get; } public AccessMode Mode { get; } public bool IsStone { get; } public bool IsHome { get; } public bool AlwaysShow { get { if (Kind != MapPinKind.Here) { return Kind == MapPinKind.Player; } return true; } } internal MapPinSpec(MapPinKind kind, PortalEntry? portal, string label, bool favourite, bool home = false, AccessMode mode = AccessMode.Public, bool stone = false) { IsStone = stone; Kind = kind; Mode = mode; Portal = portal; Label = label; IsFavourite = favourite; IsHome = home; } } public sealed class MapPinPlan { public IReadOnlyList<MapPinSpec> Pins { get; } public int DestinationCount { get; } public int HereIndex { get; } public int PlayerIndex { get; } public PortalEntry? ReturnHub { get; } public int ReturnHubIndex { get; } private MapPinPlan(IReadOnlyList<MapPinSpec> pins, int destinations, int here, int player, PortalEntry? returnHub) { Pins = pins; DestinationCount = destinations; HereIndex = here; PlayerIndex = player; ReturnHub = returnHub; ReturnHubIndex = ((returnHub != null) ? IndexOf(returnHub.Id) : (-1)); } public static MapPinPlan Build(IReadOnlyList<PickerGroup> groups, PortalEntry? current, PortalEntry? returnHub = null, string? homeHubKey = null, bool accessModesEnabled = true, bool highlightStone = false) { if (groups == null) { throw new ArgumentNullException("groups"); } string text = HomeHub.Parse(homeHubKey); List<MapPinSpec> list = new List<MapPinSpec>(); foreach (PickerGroup group in groups) { foreach (PickerRow row in group.Rows) { bool home = text != null && row.Portal.IsHub && PortalKey.Of(row.Portal.Position) == text; list.Add(new MapPinSpec(row.Portal.IsHub ? MapPinKind.Hub : MapPinKind.Portal, row.Portal, row.Label, row.IsFavourite, home, PortalAccessRules.EffectiveMode(row.Portal.Mode, accessModesEnabled), highlightStone && row.Portal.AllowsAllItems)); } } int count = list.Count; int here = -1; if (current != null) { here = list.Count; list.Add(new MapPinSpec(MapPinKind.Here, current, PickerList.LabelOf(current), favourite: false, home: false, PortalAccessRules.EffectiveMode(current.Mode, accessModesEnabled), highlightStone && current.AllowsAllItems)); } list.Add(new MapPinSpec(MapPinKind.Player, null, "", favourite: false)); return new MapPinPlan(list.AsReadOnly(), count, here, list.Count - 1, returnHub); } public PortalEntry? DestinationAt(int index) { if (index < 0 || index >= DestinationCount) { return null; } return Pins[index].Portal; } public PortalEntry? HomeCandidateAt(int index) { PortalEntry portalEntry = DestinationAt(index); if (portalEntry == null || !HomeHub.CanSet(portalEntry)) { return null; } return portalEntry; } public int IndexOf(PortalId id) { for (int i = 0; i < DestinationCount; i++) { if (Pins[i].Portal.Id.Equals(id)) { return i; } } return -1; } } public enum MapImage { GameMapWithFog, PlainWithFog, Plain } public static class MapPaneRules { public static bool UsesExploredMask(bool onlyExploredPortals) { return onlyExploredPortals; } public static MapImage Image(bool useGameMap, bool hideUnexplored) { if (!useGameMap) { if (!hideUnexplored) { return MapImage.Plain; } return MapImage.PlainWithFog; } return MapImage.GameMapWithFog; } public static bool UsesGameMaterial(MapImage image) { return image == MapImage.GameMapWithFog; } public static float StartZoom(float value) { return ThresholdSettings.Map.StartZoom.Normalize(value); } } public readonly struct MapView { public float UvX { get; } public float UvY { get; } public float UvWidth { get; } public float UvHeight { get; } public float Width { get; } public float Height { get; } public MapView(float uvX, float uvY, float uvWidth, float uvHeight, float width, float height) { UvX = uvX; UvY = uvY; UvWidth = uvWidth; UvHeight = uvHeight; Width = width; Height = height; } } public static class OrdinaryMapPins { public const float IconSize = 24f; public static bool Shows(bool allowsUi, bool active, bool showPortalPins, bool largeMap, bool inSession) { return allowsUi && active && (showPortalPins || inSession) && largeMap; } public static IReadOnlyList<MapPinSpec> Build(IReadOnlyList<PickerGroup> groups, bool accessModesEnabled, bool highlightStone = false) { MapPinPlan mapPinPlan = MapPinPlan.Build(groups, null, null, null, accessModesEnabled, highlightStone); return mapPinPlan.Pins.Take(mapPinPlan.DestinationCount).ToList().AsReadOnly(); } public static string Label(MapPinSpec pin, bool showLabels, string untagged) { if (pin == null) { throw new ArgumentNullException("pin"); } if (!showLabels) { return ""; } string text; if (pin.Label.Length <= 0) { text = untagged; if (text == null) { return ""; } } else { text = pin.Label; } return text; } public static PinPoint ToGui(float mx, float my, MapView view) { return new PinPoint((mx - view.UvX) / view.UvWidth * view.Width, (my - view.UvY) / view.UvHeight * view.Height); } public static bool Visible(float mx, float my, MapView view) { if (mx > view.UvX && mx < view.UvX + view.UvWidth && my > view.UvY) { return my < view.UvY + view.UvHeight; } return false; } } public enum IconClickAction { None, HoldTravel, PlacePin, RemovePin } public static class PickerMapClicks { public const float DoubleClickSeconds = 0.3f; public const string LeftClick = "OnMapLeftClick"; public const string RightClick = "OnMapRightClick"; public const string DoubleClick = "OnMapDblClick"; public const string RemovePinUnderPointer = "RemovePinUnderPointer"; public static readonly string[] PatchedHandlers = new string[4] { "OnMapLeftClick", "OnMapRightClick", "OnMapDblClick", "RemovePinUnderPointer" }; public static bool NativeRuns(string handler, bool pickerOpen) { if (pickerOpen && !(handler == "OnMapDblClick")) { return handler == "RemovePinUnderPointer"; } return true; } public static IconClickAction OnIcon(bool pickerOpen, bool rightButton, bool secondClick, bool pinUnderPointer) { if (!pickerOpen) { return IconClickAction.None; } if (rightButton) { if (!pinUnderPointer) { return IconClickAction.None; } return IconClickAction.RemovePin; } if (!secondClick) { return IconClickAction.HoldTravel; } return IconClickAction.PlacePin; } public static MapPoint? HereAt(bool pickerOpen, PortalEntry? source, MapPoint? player) { if (pickerOpen && source != null) { return source.Position; } return player; } } public sealed class HeldClick<T> where T : class { private T? held; private float due; public T? Held => held; public bool IsSecondClick(T target, float now, Func<T, T, bool> same) { if (held != null && now < due) { return same(held, target); } return false; } public void Hold(T target, float now) { held = target ?? throw new ArgumentNullException("target"); due = now + 0.3f; } public void Drop() { held = null; } public T? TakeDue(float now) { if (held == null || now < due) { return null; } T? result = held; held = null; return result; } } public sealed class PinLayoutCache { private readonly List<PinPoint> points = new List<PinPoint>(); private MapView view; private bool spread; private bool has; public bool Matches(MapView view, bool spread, IReadOnlyList<PinPoint> points) { if (!has || this.spread != spread || points == null || this.points.Count != points.Count || !SameView(this.view, view)) { return false; } for (int i = 0; i < points.Count; i++) { if (!this.points[i].Equals(points[i])) { return false; } } return true; } public void Remember(MapView view, bool spread, IReadOnlyList<PinPoint> points) { if (points == null) { throw new ArgumentNullException("points"); } this.view = view; this.spread = spread; this.points.Clear(); for (int i = 0; i < points.Count; i++) { this.points.Add(points[i]); } has = true; } private static bool SameView(MapView a, MapView b) { if (a.UvX == b.UvX && a.UvY == b.UvY && a.UvWidth == b.UvWidth && a.UvHeight == b.UvHeight && a.Width == b.Width) { return a.Height == b.Height; } return false; } } public readonly struct PinPoint : IEquatable<PinPoint> { public float X { get; } public float Y { get; } public PinPoint(float x, float y) { X = x; Y = y; } public bool Equals(PinPoint other) { if (X.Equals(other.X)) { return Y.Equals(other.Y); } return false; } public override bool Equals(object? obj) { if (obj is PinPoint other) { return Equals(other); } return false; } public override int GetHashCode() { return (X, Y).GetHashCode(); } public override string ToString() { return "(" + X + ", " + Y + ")"; } } public enum LabelSide { Below, Above, Left, Right } public static class PinSpread { private static int[] parents = Array.Empty<int>(); private static readonly Dictionary<int, List<int>> clusters = new Dictionary<int, List<int>>(); private static readonly List<List<int>> pool = new List<List<int>>(); private static int poolUsed; public static int ScratchGrows { get; private set; } public static IReadOnlyList<PinPoint> Spread(IReadOnlyList<PinPoint> points, float gap, bool enabled) { List<PinPoint> list = new List<PinPoint>(); List<LabelSide> sides = new List<LabelSide>(); LayoutInto(points, gap, enabled, list, sides); return list; } public static IReadOnlyList<LabelSide> LabelSides(IReadOnlyList<PinPoint> points, float gap, bool enabled) { List<PinPoint> positions = new List<PinPoint>(); List<LabelSide> list = new List<LabelSide>(); LayoutInto(points, gap, enabled, positions, list); return list; } public static void LayoutInto(IReadOnlyList<PinPoint> points, float gap, bool enabled, List<PinPoint> positions, List<LabelSide> sides) { if (points == null) { throw new ArgumentNullException("points"); } if (positions == null) { throw new ArgumentNullException("positions"); } if (sides == null) { throw new ArgumentNullException("sides"); } positions.Clear(); sides.Clear(); for (int i = 0; i < points.Count; i++) { positions.Add(points[i]); sides.Add(LabelSide.Below); } if (!enabled || points.Count < 2 || !(gap > 0f)) { return; } EnsureParents(points.Count); for (int j = 0; j < points.Count; j++) { parents[j] = j; } float num = gap * gap; for (int k = 0; k < points.Count; k++) { for (int l = k + 1; l < points.Count; l++) { float num2 = points[k].X - points[l].X; float num3 = points[k].Y - points[l].Y; if (num2 * num2 + num3 * num3 < num) { parents[Root(l)] = Root(k); } } } clusters.Clear(); poolUsed = 0; for (int m = 0; m < points.Count; m++) { int key = Root(m); if (!clusters.TryGetValue(key, out List<int> value)) { value = (clusters[key] = Rent()); } value.Add(m); } foreach (List<int> value2 in clusters.Values) { int count = value2.Count; if (count < 2) { continue; } double num4 = 0.0; double num5 = 0.0; foreach (int item in value2) { num4 += (double)points[item].X; num5 += (double)points[item].Y; } num4 /= (double)count; num5 /= (double)count; double num6 = (double)gap / (2.0 * Math.Sin(Math.PI / (double)count)); for (int n = 0; n < count; n++) { double num7 = Math.PI / 2.0 - Math.PI * 2.0 * (double)n / (double)count; double num8 = Math.Cos(num7); double num9 = Math.Sin(num7); positions[value2[n]] = new PinPoint((float)(num4 + num6 * num8), (float)(num5 + num6 * num9)); sides[value2[n]] = ((Math.Abs(num9) >= Math.Abs(num8) - 1E-06) ? ((num9 > 0.0) ? LabelSide.Above : LabelSide.Below) : ((num8 > 0.0) ? LabelSide.Right : LabelSide.Left)); } } static int Root(int num10) { while (parents[num10] != num10) { num10 = (parents[num10] = parents[parents[num10]]); } return num10; } } private static void EnsureParents(int count) { if (parents.Length < count) { int num; for (num = ((parents.Length == 0) ? count : parents.Length); num < count; num *= 2) { } parents = new int[num]; ScratchGrows++; } } private static List<int> Rent() { List<int> list; if (poolUsed < pool.Count) { list = pool[poolUsed]; } else { list = new List<int>(); pool.Add(list); ScratchGrows++; } poolUsed++; list.Clear(); return list; } } } namespace ExtraPotions.Threshold.Localization { public readonly struct TextKey { public string Key { get; } public int Arguments { get; } public TextKey(string key, int arguments) { Key = key; Arguments = arguments; } public override string ToString() { return Key; } } public static class ThresholdTextKeys { public const string OffNoCrucible = "off_no_crucible"; public const string OffNoThreshold = "off_no_threshold"; public const string PortalEditNoReply = "portal_edit_no_reply"; public const string StepAside = "step_aside"; public const string TeleportEverythingStepAside = "te_step_aside"; public const string ImportDone = "import_done"; public const string CheatIgnoreTeleportRestrictions = "cheat_ignore_teleport_restrictions"; public const string HoverWalkIn = "hover_walk_in"; public const string HoverSetTag = "hover_set_tag"; public const string HoverCycleMode = "hover_cycle_mode"; public const string HoverHub = "hover_hub"; public const string PickerTeleport = "picker_teleport"; public const string PickerCancel = "picker_cancel"; public const string PickerUnnamed = "picker_unnamed"; public const string HoverTarget = "hover_target"; public const string PickerTitle = "picker_title"; public const string PickerSearch = "picker_search"; public const string PickerTravel = "picker_travel"; public const string PickerReturnToHub = "picker_return_to_hub"; public const string PickerSetHome = "picker_set_home"; public const string PickerClearHome = "picker_clear_home"; public const string PickerHomeHub = "picker_home_hub"; public const string PickerSetHub = "picker_set_hub"; public const string PickerClearHub = "picker_clear_hub"; public const string PickerHubName = "picker_hub_name"; public const string PickerClose = "picker_close"; public const string PickerUntagged = "picker_untagged"; public const string PickerDistance = "picker_distance"; public const string PickerCarryWarning = "picker_carry_warning"; public const string PickerEmpty = "picker_empty"; public const string PickerFavouriteLabel = "picker_favorite_label"; public const string PickerToggleFavourite = "picker_toggle_favorite"; public const string MenuPortalPinsOn = "menu_portal_pins_on"; public const string MenuPortalPinsOff = "menu_portal_pins_off"; public const string MapYouAreHere = "map_you_are_here"; public const string PickerMapPinsHint = "picker_map_pins_hint"; public const string FuelNotEnough = "fuel_not_enough"; public const string CarryBlocked = "carry_blocked"; public const string CartBlocked = "cart_blocked"; public const string CartStays = "cart_stays"; public const string SgPickerTitle = "sg_picker_title"; public const string SgPickerEmpty = "sg_picker_empty"; public const string SgSailOn = "sg_sail_on"; public const string SgCrewWait = "sg_crew_wait"; public const string SgHelmsman = "sg_helmsman"; private static readonly TextKey[] SeaGateWords = new TextKey[30] { new TextKey("sg_pillar", 0), new TextKey("sg_pillar_desc", 0), new TextKey("sg_default_name", 0), new TextKey("sg_unpaired", 0), new TextKey("sg_hover_rename", 0), new TextKey("sg_hover_sailin", 0), new TextKey("sg_rename", 0), new TextKey("sg_pair_ok", 0), new TextKey("sg_pair_far", 2), new TextKey("sg_pair_close", 2), new TextKey("sg_pair_height", 2), new TextKey("sg_pair_nowater", 0), new TextKey("sg_pair_shallow", 2), new TextKey("sg_pair_deep", 2), new TextKey("sg_paired", 0), new TextKey("sg_fix_hint", 0), new TextKey("sg_denied_gone", 0), new TextKey("sg_denied_cargo", 0), new TextKey("sg_denied_notready", 0), new TextKey("sg_denied_nohelm", 0), new TextKey("sg_fallback", 0), new TextKey("sg_denied_closed", 0), new TextKey("sg_denied_private", 0), new TextKey("sg_denied_admin", 0), new TextKey("sg_notowner", 0), new TextKey("sg_picker_title", 0), new TextKey("sg_picker_empty", 0), new TextKey("sg_sail_on", 0), new TextKey("sg_crew_wait", 1), new TextKey("sg_helmsman", 0) }; private static readonly TextKey[] Fixed = new TextKey[48] { new TextKey("off_no_crucible", 0), new TextKey("off_no_threshold", 0), new TextKey("portal_edit_no_reply", 0), new TextKey("step_aside", 1), new TextKey("te_step_aside", 0), new TextKey("import_done", 2), new TextKey("cheat_ignore_teleport_restrictions", 0), new TextKey("hover_walk_in", 0), new TextKey("hover_set_tag", 1), new TextKey("hover_cycle_mode", 2), new TextKey("hover_hub", 1), new TextKey("picker_teleport", 0), new TextKey("picker_cancel", 0), new TextKey("picker_unnamed", 0), new TextKey("hover_target", 0), new TextKey("picker_title", 0), new TextKey("picker_search", 0), new TextKey("picker_travel", 0), new TextKey("picker_return_to_hub", 1), new TextKey("picker_set_home", 0), new TextKey("picker_clear_home", 0), new TextKey("picker_home_hub", 0), new TextKey("picker_set_hub", 0), new TextKey("picker_clear_hub", 0), new TextKey("picker_hub_name", 0), new TextKey("picker_close", 0), new TextKey("picker_untagged", 0), new TextKey("picker_distance", 1), new TextKey("picker_carry_warning", 0), new TextKey("picker_empty", 0), new TextKey("picker_favorite_label", 2), new TextKey("picker_toggle_favorite", 0), new TextKey("menu_portal_pins_on", 0), new TextKey("menu_portal_pins_off", 0), new TextKey("map_you_are_here", 0), new TextKey("picker_map_pins_hint", 0), new TextKey("fuel_not_enough", 2), new TextKey("carry_blocked", 0), new TextKey("cart_blocked", 0), new TextKey("cart_stays", 0), new TextKey("picker_search_help", 0), new TextKey("picker_destination_help", 0), new TextKey("picker_category_help", 0), new TextKey("picker_favorite_help", 0), new TextKey("picker_home_help", 0), new TextKey("picker_source_hub_help", 0), new TextKey("picker_return_help", 0), new TextKey("picker_close_help", 0) }; public static IReadOnlyList<TextKey> All { get; } = Array.AsReadOnly(Fixed.Concat(SeaGateWords).Concat(from k in Enum.GetValues(typeof(PickerCategory)).Cast<PickerCategory>().Select(Category) .Concat(Enum.GetValues(typeof(AccessMode)).Cast<AccessMode>().Select(Mode)) .Concat((from HubRefusal r in Enum.GetValues(typeof(HubRefusal)) where r != HubRefusal.None select r).Select(Refusal)) .Concat((from RouteRefusal r in Enum.GetValues(typeof(RouteRefusal)) where r != RouteRefusal.None select r).Select(Refusal)) .Concat((from EditRefusal r in Enum.GetValues(typeof(EditRefusal)) where r != EditRefusal.None && !Enum.IsDefined(typeof(HubRefusal), r.ToString()) select r).Select(Refusal)) .Concat(Enum.GetValues(typeof(ImportNote)).Cast<ImportNote>().Select(Note)) select new TextKey(k, ArgumentsOf(k))).ToArray()); public static string Category(PickerCategory category) { return "category_" + Snake(category.ToString()); } public static string Mode(AccessMode mode) { return "mode_" + Snake(mode.ToString()); } public static string Refusal(HubRefusal refusal) { return "refusal_" + Snake(refusal.ToString()); } public static string Refusal(RouteRefusal refusal) { return "refusal_" + Snake(refusal.ToString()); } public static string Refusal(EditRefusal refusal) { return "refusal_" + Snake(refusal.ToString()); } public static string Note(ImportNote note) { return "import_note_" + Snake(note.ToString()); } private static int ArgumentsOf(string key) { return (key == "refusal_limit_reached") ? 1 : 0; } private static string Snake(string name) { StringBuilder stringBuilder = new StringBuilder(name.Length + 8); for (int i = 0; i < name.Length; i++) { if (char.IsUpper(name[i]) && i > 0) { stringBuilder.Append('_'); } stringBuilder.Append(char.ToLowerInvariant(name[i])); } return stringBuilder.ToString(); } } } namespace ExtraPotions.Threshold.Integration { public enum HookKind { Method, Field } public enum HookStatus { Wired, Pending } public sealed class HookTarget { public string Module { get; } public string Type { get; } public string Member { get; } public HookKind Kind { get; } public string Signature { get; } public HookStatus Status { get; } public string Purpose { get; } internal HookTarget(string module, string type, string member, HookKind kind, string signature, HookStatus status, string purpose) { Module = module; Type = type; Member = member; Kind = kind; Signature = signature; Status = status; Purpose = purpose; } public override string ToString() { return Type + "." + Member + ((Kind == HookKind.Method) ? ("(" + Signature + ")") : (" : " + Signature)); } } public static class HookTargets { public static IReadOnlyList<HookTarget> All { get; } = Array.AsReadOnly(new HookTarget[91] { WM("Picker", "TeleportWorld", "Interact", "Humanoid,Boolean,Boolean", "prefix: Use keeps the vanilla tag input; the mode cycle key cycles the access mode; the tag edit key on a hub edits its name (A5, U1, U6, T12b)"), WM("Picker", "TeleportWorld", "GetHoverText", "", "postfix: hover lines: hub name, walk in to choose, access mode, hub rename (T12b)"), WM("Picker", "TeleportWorld", "Awake", "", "postfix: register the owner-side RPC for the fields the server decided (T12b)"), M("Picker", "TeleportWorld", "GetText", "", "portal tag"), WM("Picker", "TeleportWorld", "SetText", "String", "prefix: a tag change on a Private or Admin portal goes to the server, owner or admin only (runtime decision 7)"), WM("Travel", "TeleportWorld", "Teleport", "Player", "prefix: walking into any portal opens the map picker (U1, follow-up); the map's Return To Hub button goes to the player's home hub (A4, B2, follow-ups 2 and 3)"), WM("Travel", "TeleportWorld", "HaveTarget", "", "postfix: connected glow for every portal, whose target the map picks (T12)"), WM("Travel", "TeleportWorld", "TargetFound", "", "postfix: every portal shows its swirl, as Wayfare (T12)"), WF("Travel", "TeleportWorld", "m_nview", "ZNetView", "the portal's own ZDO: id, tag, mode, owner, hub, facing and tag pair (T12)"), WF("Travel", "TeleportWorld", "m_exitDistance", "Single", "exit point in front of the target portal (T12)"), WF("Travel", "TeleportWorld", "m_allowAllItems", "Boolean", "carry rule: the portal walked into decides (U4, T12)"), WM("Travel", "Player", "TeleportTo", "Vector3,Quaternion,Boolean", "the client teleports to the clicked portal on the game's own path with fade and loading (C3, U3, T12); a true answer starts a quick jump (T14)"), WM("Travel", "Inventory", "IsTeleportable", "Boolean", "carry rule (C1): the Boolean is allowAllItems (ILSpy read); true leaves only the game's hard block (T12)"), WM("Travel", "Inventory", "CountItems", "String,Int32,Boolean", "fuel count by the item's shared name (T12)"), WM("Travel", "Inventory", "RemoveItem", "String,Int32,Int32,Boolean", "fuel consumption after the game accepted the teleport (T12)"), WM("List", "ZDOMan", "GetPortalList", "", "server portal list (D1, T11)"), WM("List", "ZDOMan", "AddIfPortal", "ZDO,Int32", "Wayfare discovery: move widened prefabs into the portal list (T11)"), WF("List", "ZDOMan", "m_objectsByID", "Dictionary`2<ZDOID,ZDO>", "Wayfare discovery: reclassify loaded ZDOs (T11)"), WM("List", "Game", "get_PortalPrefabHash", "", "Wayfare discovery: widen the List<int> property to every TeleportWorld prefab (A6, T11)"), WM("List", "ZDOMan", "LoadChunks", "String,FileHelpers/FileSource,Version/World", "postfix: reclassify widened portals of a chunked world save (T11)"), WM("List", "ZDOMan", "Load", "BinaryReader,Version/World", "postfix: reclassify widened portals of a single-file world save (T11)"), M("List", "Game", "ConnectPortals", "", "vanilla tag pairing, read only, never patched"), WM("Map", "Minimap", "IsExplored", "Vector3", "Only Explored Portals (A7, the picker list and the map pane, T13b)"), WM("Map", "Minimap", "WorldToMapPoint", "Vector3,Single&,Single&", "map pane pin and player arrow positions (TH06, T13b)"), WF("Map", "Minimap", "m_mapTexture", "Texture2D", "map pane image, the game's own map at runtime (TH06, T13b)"), WF("Map", "Minimap", "m_fogTexture", "Texture2D", "map pane fog of war over unexplored land (TH06, T13b)"), WF("Map", "Minimap", "m_largeMarker", "RectTransform", "map pane player arrow sprite, the game's own (TH06, T13b)"), WF("Map", "Piece", "m_icon", "Sprite", "map pane portal pin sprite, the portal piece's own icon (TH06, T13b)"), WF("Map", "Minimap", "m_pinRootLarge", "RectTransform", "ordinary map pins: Threshold's layer sits beside the game's large-map pin root (#13)"), WF("Map", "Minimap", "m_mapImageLarge", "RawImage", "ordinary map pins: the large map's view rectangle and size (#13)"), WF("Map", "Minimap", "m_mode", "Minimap/MapMode", "ordinary map pins: drawn only while the large map is open (#13)"), WF("Map", "Minimap", "m_icons", "List`1<Minimap/SpriteData>", "ordinary map pins: the game's own map portal pin glyph (PinType.Icon4), as Wayfare's icons (#13)"), M("Travel", "TeleportWorldTrigger", "OnTriggerEnter", "Collider", "the walk-in trigger; its collider tells when the player stepped out and the picker closes (#114)"), WF("Favorites", "Player", "m_customData", "Dictionary`2<String,String>", "favorites and the per-world home hub in character data"), WM("Favorites", "ZNet", "GetWorldUID", "", "world id of the home hub's character data key (Threshold follow-up 2)"), M("Hubs", "Piece", "GetCreator", "", "Hub Editing CreatorOrAdmin (B4)"), WM("Access", "Piece", "SetCreator", "Int64,PlatformUserID", "prefix and postfix: a portal placed now gets the Default Access Mode (runtime decision 2)"), WM("Jumps", "Player", "UpdateTeleport", "Single", "prefix: Quick Portals hurries the timer of a jump Threshold started (T14, U5)"), WF("Jumps", "Player", "m_teleporting", "Boolean", "quick jump: the local player is jumping (T14)"), WF("Jumps", "Player", "m_distantTeleport", "Boolean", "quick jump: a long jump, the only kind hurried (T14)"), WF("Jumps", "Player", "m_teleportTimer", "Single", "quick jump: the game's jump timer, pushed by JumpRules.Step (T14)"), WF("Jumps", "Player", "m_teleportFromPos", "Vector3", "quick jump: start of the jump for its map distance (T14)"), WF("Jumps", "Player", "m_teleportTargetPos", "Vector3", "quick jump: target of the jump and of the area checks (T14)"), WM("Jumps", "Hud", "UpdateBlackScreen", "Player,Single", "prefix: Screen Only When Loading keeps a jump into a loaded area clear (T14)"), WF("Jumps", "Hud", "m_loadingScreen", "CanvasGroup", "quick jump screen fade (T14)"), WF("Jumps", "Hud", "m_haveSetupLoadScreen", "Boolean", "quick jump screen: the game's own fade-out state (T14)"), WM("Jumps", "ZNetScene", "IsAreaReady", "Vector3", "quick jump: the target area is loaded already (T14)"), WM("Jumps", "ZoneSystem", "Update", "", "postfix: Quick Area Loading loads the land faster during a Threshold jump (T14)"), WM("Jumps", "ZoneSystem", "CreateLocalZones", "Vector3", "Quick Area Loading: the game's own zone step, called again within a budget (T14)"), WM("Jumps", "ZoneSystem", "IsActiveAreaLoaded", "", "Quick Area Loading: stop once the area is in (T14)"), WM("Jumps", "ZNetScene", "CreateDestroyObjects", "", "postfix: Quick Area Loading creates more listed objects during a Threshold jump (T14)"), WM("Jumps", "ZNetScene", "CreateObject", "ZDO", "Quick Area Loading: the game's own object creation (T14)"), WM("Jumps", "ZNetScene", "ZDOCompare", "ZDO,ZDO", "Quick Area Loading: the game's own creation order (T14)"), WF("Jumps", "ZNetScene", "m_tempCurrentObjects", "List`1<ZDO>", "Quick Area Loading: the objects the game has just listed (T14)"), WM("SeaGates", "ZRoutedRpc", ".ctor", "Boolean", "postfix: the sea gate list, grant and crew jump calls for every session (T14)"), WM("SeaGates", "ZNetScene", "Awake", "", "postfix: the Sea Gate Pillar prefab joins the scene (T14, U8)"), WM("SeaGates", "ObjectDB", "Awake", "", "postfix: the pillar's recipe and its place in the hammer (T14)"), WM("SeaGates", "Player", "UpdatePlacementGhost", "Boolean", "postfix: a pillar ghost deeper than 2 m under water is invalid (T14)"), WM("SeaGates", "Player", "TryPlacePiece", "Piece", "postfix: say why a pillar was refused (T14)"), WM("SeaGates", "ZNetScene", "Destroy", "GameObject", "prefix: a pillar destroyed for real unpairs its partner (T14)"), WM("SeaGates", "Game", "OnDestroy", "", "postfix: a world unload drops grants and gate surfaces (T14)"), WM("SeaGates", "Ship", "Awake", "", "postfix: the ship's landed and pick calls, the ship list (T14)"), WM("SeaGates", "Ship", "CustomFixedUpdate", "Single", "prefix: a ship stopped by a gate stays still (T14)"), WM("SeaGates", "WearNTear", "ApplyDamage", "Single,HitData", "prefix: a stopped or protected ship takes no damage (T14)"), WM("SeaGates", "Player", "OnDeath", "", "prefix: death ends a crew hold (T14)"), WM("SeaGates", "Player", "OnDestroy", "", "prefix: logout ends a crew hold (T14)"), WM("SeaGates", "PlayerProfile", "SaveLogoutPoint", "", "postfix: a player held mid-jump logs out beside the destination gate (T14)"), WM("SeaGates", "Character", "RPC_Damage", "Int64,HitData", "prefix: no damage for a few seconds after a sea gate landing (T14)"), WM("SeaGates", "Character", "ApplyDamage", "HitData,Boolean,Boolean,HitData/DamageModifier", "prefix: no damage ticks after a sea gate landing (T14)"), WM("Travel", "Inventory", "GetAllItems", "", "Teleport Listed Items: the restricted items carried, by prefab name (T17)"), WM("Tames", "Character", "GetAllCharacters", "", "Bring Tames: creatures near the traveler (T18)"), WM("Tames", "MonsterAI", "GetFollowTarget", "", "Bring Tames: the game's follow command targets the traveler (T18)"), WM("Tames", "ZDOMan", "GetSessionID", "", "Bring Tames: this client takes ownership of the creatures it moves (T18)"), WM("Tames", "ZDO", "SetOwner", "Int64", "Bring Tames: ownership before the move (T18)"), WM("Tames", "ZDO", "SetPosition", "Vector3", "Bring Tames: the creature's new place; an unloaded one is rebuilt there by streaming (T18)"), WM("Tames", "ZNetScene", "FindInstance", "ZDO", "Bring Tames: a creature still loaded after a short jump is moved directly (T18)"), WM("Arrival", "MonsterAI", "UpdateTarget", "Humanoid,Single,Boolean&,Boolean&", "postfix: Arrival Protection Seconds, creatures drop a player who has just come through a portal"), WF("Arrival", "MonsterAI", "m_targetCreature", "Character", "Arrival Protection Seconds: the creature's current target"), WF("Arrival", "Character", "m_nview", "ZNetView", "Arrival Protection Seconds: the player's ZDO carries the protection end time"), WM("Map", "Minimap", "InTextInput", "", "Portal Pins Key: no toggle while a map pin name is typed"), WF("Carts", "Vagon", "m_instances", "List`1<Vagon>", "Bring Carts: the loaded carts, to find the one the traveler pulls"), WM("Carts", "Vagon", "IsAttached", "Character", "Bring Carts: the cart is pulled by the traveler"), WM("Carts", "Vagon", "Detach", "", "Bring Carts: the cart is let go before it is moved"), WF("Carts", "Vagon", "m_container", "Container", "Bring Carts: the cart's box and load"), WF("Carts", "Vagon", "m_nview", "ZNetView", "Bring Carts: the cart's ZDO, which holds its load and is moved whole"), WM("Carts", "Vagon", "RPC_RequestOwn", "Int64", "prefix: Bring Carts, nobody takes over a cart while it travels"), WM("Carts", "Container", "GetInventory", "", "Bring Carts: the cart's load, checked with the carry rule"), WM("Carts", "Container", "IsInUse", "", "Bring Carts: a cart whose box is open stays"), WM("Carts", "Container", "RPC_RequestOpen", "Int64,Int64", "prefix: Bring Carts, nobody opens a cart's box while it travels"), WM("Carts", "ZDO", "SetRotation", "Quaternion", "Bring Carts: the cart faces the destination's way"), WM("Carts", "ZDO", "HasOwner", "", "Bring Carts: a cart another player took over is left alone") }); private static HookTarget M(string module, string type, string member, string parameters, string purpose) { return new HookTarget(module, type, member, HookKind.Method, parameters, HookStatus.Pending, purpose); } private static HookTarget F(string module, string type, string member, string fieldType, string purpose) { return new HookTarget(module, type, member, HookKind.Field, fieldType, HookStatus.Pending, purpose); } private static HookTarget WM(string module, string type, string member, string parameters, string purpose) { return new HookTarget(module, type, member, HookKind.Method, parameters, HookStatus.Wired, purpose); } private static HookTarget WF(string module, string type, string member, string fieldType, string purpose) { return new HookTarget(module, type, member, HookKind.Field, fieldType, HookStatus.Wired, purpose); } } public sealed class SourcePlugin { public string Guid { get; } public string Name { get; } internal SourcePlugin(string guid, string name) { Guid = guid; Name = name; } } public sealed class StepAsideDecision { public bool SteppedAside => Installed.Count > 0; public IReadOnlyList<SourcePlugin> Installed { get; } public string SourceNames { get; } internal StepAsideDecision(IReadOnlyList<SourcePlugin> installed) { Installed = installed; SourceNames = string.Join(" and ", installed.Select((SourcePlugin s) => s.Name).Distinct()); } } public static class ProductStepAside { public const string NoticeKey = "step_aside"; public static readonly SourcePlugin PortalSelector = new SourcePlugin("lazly.valheim.portalselector", "PortalSelector"); public static readonly SourcePlugin PotalMap = new SourcePlugin("com.korcaptain.potalmap", "PotalMap"); public static readonly SourcePlugin PotalMapRenamed = new SourcePlugin("com.korcaptain.portalmap", "PotalMap"); public static readonly SourcePlugin Wayfare = new SourcePlugin("com.Wayfare", "Wayfare"); public static IReadOnlyList<SourcePlugin> Sources { get; } = Array.AsReadOnly(new SourcePlugin[4] { PortalSelector, PotalMap, PotalMapRenamed, Wayfare }); public static StepAsideDecision Decide(IEnumerable<string> installedGuids) { if (installedGuids == null) { throw new ArgumentNullException("installedGuids"); } HashSet<string> installed = new HashSet<string>(installedGuids.Where((string g) => g != null), StringComparer.Ordinal); return new StepAsideDecision(Sources.Where((SourcePlugin s) => installed.Contains(s.Guid)).ToList()); } } public static class TeleportEverythingStepAside { public const string Guid = "odinplus.TeleportEverything"; public const string Name = "TeleportEverything"; public static bool Installed(IEnumerable<string> installedGuids) { if (installedGuids == null) { throw new ArgumentNullException("installedGuids"); } return installedGuids.Any((string g) => g != null && (string.Equals(g, "odinplus.TeleportEverything", StringComparison.OrdinalIgnoreCase) || g.StartsWith("odinplus.TeleportEverything.", StringComparison.OrdinalIgnoreCase))); } public static bool ExtrasActive(bool teleportEverythingInstalled, bool thresholdActive) { if (thresholdActive) { return !teleportEverythingInstalled; } return false; } } } namespace ExtraPotions.Threshold.Conversion { public readonly struct SourceEntry { public string Section { get; } public string Key { get; } public string Value { get; } public SourceEntry(string section, string key, string value) { Section = section; Key = key; Value = value; } } public static class SourceConfig { public static IReadOnlyList<SourceEntry> Parse(string? text) { List<SourceEntry> list = new List<SourceEntry>(); if (string.IsNullOrEmpty(text)) { return list; } string section = ""; string[] array = text.Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal)) { continue; } if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal)) { section = text2.Substring(1, text2.Length - 2).Trim(); continue; } int num = text2.IndexOf('='); if (num > 0) { list.Add(new SourceEntry(section, text2.Substring(0, num).Trim(), text2.Substring(num + 1).Trim())); } } return list; } } public enum ImportNote { LanguageNotImported, FontNotImported, HubNotFound, CheatAppliesWhereAllowed, InvalidValue } public sealed class ImportItem { public ThresholdSetting Setting { get; } public string Value { get; } public string Source { get; } internal ImportItem(ThresholdSetting setting, string value, string source) { Setting = setting; Value = value; Source = source; } } public sealed class ImportPlan { public IReadOnlyList<ImportItem> Items { get; } public IReadOnlyList<ImportNote> Notes { get; } internal ImportPlan(IReadOnlyList<ImportItem> items, IReadOnlyList<ImportNote> notes) { Items = items; Notes = notes; } } public static class ImportMarker { public static IReadOnlyList<string> Parse(string? value) { return (from p in (value ?? "").Split(';') select p.Trim() into p where p.Length > 0 select p).Distinct<string>(StringComparer.Ordinal).ToList(); } public static string Add(string? value, string source) { return string.Join(";", Parse(value).Concat(new string[1] { source.Trim() }).Distinct<string>(StringComparer.Ordinal)); } public static bool Contains(string? value, string source) { return Parse(value).Contains<string>(source.Trim(), StringComparer.Ordinal); } } public static class PortalSelectorImport { public const string FileName = "lazly.valheim.portalselector.cfg"; public const string SourceGuid = "lazly.valheim.portalselector"; public const string SourceName = "PortalSelector"; public const string RegistrationId = "threshold.PortalSelector"; public static IReadOnlyDictionary<string, string> Settings(ImportPlan plan, string? markerBefore) { if (plan == null) { throw new ArgumentNullException("plan"); } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); if (plan.Items.Count == 0) { return dictionary; } foreach (ImportItem item in plan.Items) { dictionary[item.Setting.Id] = item.Value; } dictionary[ThresholdSettings.Internal.ImportedFrom.Id] = ImportMarker.Add(markerBefore, "PortalSelector"); return dictionary; } public static IReadOnlyList<string> PreviewLines(ImportPlan plan, string? markerBefore, Func<ImportNote, string> noteText) { if (noteText == null) { throw new ArgumentNullException("noteText"); } return plan.Notes.Select(noteText).Concat(from p in Settings(plan, markerBefore) select "Set " + p.Key + " = " + p.Value).ToList(); } public static ImportPlan Plan(string? configText) { IReadOnlyList<SourceEntry> entries = SourceConfig.Parse(configText); List<ImportItem> items = new List<ImportItem>(); List<ImportNote> list = new List<ImportNote>(); if (entries.Count == 0) { return new ImportPlan(items, list); } string text = Get("General", "PortalPrefabs"); if (text != null) { Add(ThresholdSettings.General.PortalPrefabs, PrefabFilter.IsPortalSelectorDefault(text) ? "" : ThresholdSettings.NameList(text), "General.PortalPrefabs"); } string text2 = Get("General", "ReturnToMain"); if (text2 != null) { if (Enum.TryParse<ReturnToHubMode>(text2, ignoreCase: false, out var result) && Enum.IsDefined(typeof(ReturnToHubMode), result) && !int.TryParse(text2, out var _)) { Add(ThresholdSettings.Hubs.ReturnToHub, result.ToString(), "General.ReturnToMain"); } else { list.Add(ImportNote.InvalidValue); } } bool? flag = Bool(Get("General", "RespectTeleportRestrictions")); if (flag.HasValue) { Add(ThresholdSettings.Cheats.IgnoreTeleportRestrictions, flag.Value ? "false" : "true", "General.RespectTeleportRestrictions"); if (!flag.Value) { list.Add(ImportNote.CheatAppliesWhereAllowed); } } (string, ThresholdSetting<bool>)[] array = new(string, ThresholdSetting<bool>)[3] { ("UseGameMap", ThresholdSettings.Map.UseGameMap), ("HideUnexploredMap", ThresholdSettings.Map.HideUnexploredMap), ("ShowMapLabels", ThresholdSettings.Map.ShowMapLabels) }; for (int result2 = 0; result2 < array.Length; result2++) { (string, ThresholdSetting<bool>) tuple = array[result2]; string item = tuple.Item1; ThresholdSetting<bool> item2 = tuple.Item2; bool? flag2 = Bool(Get("Map", item)); if (flag2.HasValue) { Add(item2, flag2.Value ? "true" : "false", "Map." + item); } } if (Get("UI", "Language") != null) { list.Add(ImportNote.LanguageNotImported); } if (Get("UI", "UseGameFont") != null) { list.Add(ImportNote.FontNotImported); } list.Add(ImportNote.HubNotFound); return new ImportPlan(items, list); void Add(ThresholdSetting setting, string value, string source) { items.Add(new ImportItem(setting, value, source)); } string? Get(string section, string key) { return (from e in entries where e.Section == section && e.Key == key select e.Value).FirstOrDefault(); } } private static bool? Bool(string? value) { if (value == null) { return null; } if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase)) { return false; } return null; } } public static class WayfareImport { public const string SourceGuid = "com.Wayfare"; public const string SourceName = "Wayfare"; public const string FileName = "com.Wayfare.cfg"; public const string RegistrationId = "threshold.Wayfare"; public static ImportPlan Plan(string? text) { string text2 = (from entry in SourceConfig.Parse(text) where entry.Section == "General" && entry.Key == "Teleport Mode" select entry.Value).FirstOrDefault(); List<ImportItem> list = new List<ImportItem>(); List<ImportNote> list2 = new List<ImportNote>(); if (text2 != null) { if (Enum.TryParse<TeleportMode>(text2, ignoreCase: false, out var result) && Enum.IsDefined(typeof(TeleportMode),
plugins/Threshold/Threshold.dll
Decompiled 4 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.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExtraPotions.Meadworks.Crucible; using ExtraPotions.Meadworks.Crucible.UI; using ExtraPotions.Meadworks.Crucible.UI.WindowInput; using ExtraPotions.Threshold.Access; using ExtraPotions.Threshold.Config; using ExtraPotions.Threshold.Conversion; using ExtraPotions.Threshold.Integration; using ExtraPotions.Threshold.Localization; using ExtraPotions.Threshold.Map; using ExtraPotions.Threshold.Picker; using ExtraPotions.Threshold.Portals; using ExtraPotions.Threshold.Routing; using ExtraPotions.Threshold.Runtime.Conversion; using ExtraPotions.Threshold.Runtime.Map; using ExtraPotions.Threshold.Runtime.Portals; using ExtraPotions.Threshold.Runtime.SeaGates; using ExtraPotions.Threshold.Runtime.Travel; using ExtraPotions.Threshold.SeaGates; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.3.10.0")] [assembly: AssemblyInformationalVersion("0.3.10+9cc3a345b81f6847a536e593a0f8aa83a8c9e290")] [assembly: AssemblyProduct("Threshold")] [assembly: AssemblyTitle("Threshold")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.10.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 ExtraPotions.Threshold.Runtime { public sealed class ThresholdConfig { private const string Product = "com.meadworks.threshold"; private readonly ConfigFile file; private readonly Dictionary<ThresholdSetting, ConfigEntryBase> entries = new Dictionary<ThresholdSetting, ConfigEntryBase>(); private ThresholdPlayerLocks? locks; public bool IgnoreTeleportRestrictions => Get<bool>(Cheats.IgnoreTeleportRestrictions); private ThresholdConfig(ConfigFile file, MeadworksLog log) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown ThresholdConfig thresholdConfig = this; this.file = file; bool saveOnConfigSet = file.SaveOnConfigSet; file.SaveOnConfigSet = false; try { foreach (ThresholdSetting item in ThresholdSettings.All) { BindSetting(item); } MeadworksConfig.EnsureSchema(file, 1, (IReadOnlyList<MigrationStep>)(object)new MigrationStep[1] { new MigrationStep(0, 1, (Action)delegate { thresholdConfig.MigrateDefaults(log); }) }); } finally { file.SaveOnConfigSet = saveOnConfigSet; } file.Save(); } public static ThresholdConfig Bind(ConfigFile file, MeadworksLog log) { //IL_0070: 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) if (file == null) { throw new ArgumentNullException("file"); } if (log == null) { throw new ArgumentNullException("log"); } ThresholdConfig thresholdConfig = new ThresholdConfig(file, log); foreach (KeyValuePair<ThresholdSetting, ConfigEntryBase> item in thresholdConfig.entries.Where<KeyValuePair<ThresholdSetting, ConfigEntryBase>>((KeyValuePair<ThresholdSetting, ConfigEntryBase> p) => (int)p.Key.Scope != 0 && !p.Key.Hidden)) { MeadworksBlockedOptions.Track("com.meadworks.threshold", item.Value, Map(item.Key.Scope), true); } Dictionary<string, ConfigEntryBase> locals = thresholdConfig.entries.Where<KeyValuePair<ThresholdSetting, ConfigEntryBase>>((KeyValuePair<ThresholdSetting, ConfigEntryBase> p) => ThresholdSettings.LockableLocalIds.Contains(p.Key.Id)).ToDictionary<KeyValuePair<ThresholdSetting, ConfigEntryBase>, string, ConfigEntryBase>((KeyValuePair<ThresholdSetting, ConfigEntryBase> p) => p.Key.Id, (KeyValuePair<ThresholdSetting, ConfigEntryBase> p) => p.Value, StringComparer.Ordinal); thresholdConfig.locks = new ThresholdPlayerLocks(file, (ConfigEntry<string>)(object)thresholdConfig.Entry((ThresholdSetting)(object)General.LockedPlayerSettings), locals, log); log.Verbose("Configuration bound: " + Count((SettingScope)0) + " Local, " + Count((SettingScope)1) + " ServerAuthoritative, " + Count((SettingScope)2) + " Cheat, " + ThresholdSettings.All.Count((ThresholdSetting s) => s.Hidden) + " hidden."); return thresholdConfig; } private void MigrateDefaults(MeadworksLog log) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); foreach (KeyValuePair<ThresholdSetting<string>, string> change in ThresholdDefaultsMigration.Changes) { ConfigEntry<KeyboardShortcut> val = (ConfigEntry<KeyboardShortcut>)(object)entries[(ThresholdSetting)(object)change.Key]; ThresholdSetting<string> key = change.Key; KeyboardShortcut value = val.Value; string text = ThresholdDefaultsMigration.Migrate(key, ((KeyboardShortcut)(ref value)).Serialize()); if (text != null) { val.Value = KeyboardShortcut.Deserialize(text); list.Add(((ThresholdSetting)change.Key).Id); } } if (list.Count > 0) { log.Info("Moved unchanged old input defaults to the new defaults: " + string.Join(", ", list) + ". Customized values were kept."); } } public static ConfigScope Map(SettingScope scope) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown return (ConfigScope)((int)scope switch { 0 => 0, 1 => 1, 2 => 2, _ => throw new ArgumentException("Unknown scope.", "scope"), }); } public ConfigEntryBase Entry(ThresholdSetting setting) { if (!entries.TryGetValue(setting ?? throw new ArgumentNullException("setting"), out ConfigEntryBase value)) { throw new ArgumentException("Not a Threshold setting.", "setting"); } return value; } public T Get<T>(ThresholdSetting<T> setting) { //IL_0031: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 if (((ThresholdSetting)setting).IsShortcut) { throw new ArgumentException(((ThresholdSetting)setting).Id + " is a hotkey; use Shortcut.", "setting"); } ConfigEntry<T> val = (ConfigEntry<T>)(object)Entry((ThresholdSetting)(object)setting); SettingScope scope = ((ThresholdSetting)setting).Scope; if ((int)scope != 1) { if ((int)scope == 2) { return ThresholdCheats.Effective<T>(setting, CheatLocked(), val.Value); } return setting.Normalize((locks != null) ? locks.Effective(val, val.Value) : val.Value); } return setting.Normalize(MeadworksConfig.Effective<T>(val, "com.meadworks.threshold", (ConfigScope)1, setting.Default, true)); } public KeyboardShortcut Shortcut(ThresholdSetting<string> hotkey) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (hotkey == null || !((ThresholdSetting)hotkey).IsShortcut) { throw new ArgumentException("Expected a Threshold hotkey.", "hotkey"); } ConfigEntry<KeyboardShortcut> val = (ConfigEntry<KeyboardShortcut>)(object)Entry((ThresholdSetting)(object)hotkey); if (locks == null) { return val.Value; } return locks.Effective<KeyboardShortcut>(val, val.Value); } public void Tick() { locks?.Tick(); } public bool TryUseCheat(ThresholdSetting cheat, string? label = null) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if (cheat == null || (int)cheat.Scope != 2) { throw new ArgumentException("Expected a Threshold cheat.", "cheat"); } return MeadworksBlockedOptions.TryUse("com.meadworks.threshold", cheat.Id, (ConfigScope)2, label, true); } public void Shutdown() { locks?.Dispose(); locks = null; foreach (ConfigEntryBase value in entries.Values) { MeadworksBlockedOptions.Untrack(value); } entries.Clear(); } private static bool CheatLocked() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 return (int)MeadworksAuthority.Decide("com.meadworks.threshold", (ConfigScope)2, true).Source == 2; } private void BindSetting(ThresholdSetting setting) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) ConfigEntryBase value; if (!(setting is ThresholdSetting<string> val)) { if (!(setting is ThresholdSetting<bool> setting2)) { if (!(setting is ThresholdSetting<int> val2)) { if (!(setting is ThresholdSetting<float> val3)) { if (!(setting is ThresholdSetting<ReturnToHubMode> setting3)) { if (!(setting is ThresholdSetting<HubEditing> setting4)) { if (!(setting is ThresholdSetting<SortOrder> setting5)) { if (!(setting is ThresholdSetting<TeleportMode> setting6)) { if (!(setting is ThresholdSetting<OreTransportMode> setting7)) { if (!(setting is ThresholdSetting<AccessMode> setting8)) { throw new NotSupportedException(setting.Id + ": unsupported value type " + setting.ValueType.Name + "."); } value = (ConfigEntryBase)(object)Bind<AccessMode>(setting8, null); } else { value = (ConfigEntryBase)(object)Bind<OreTransportMode>(setting7, null); } } else { value = (ConfigEntryBase)(object)Bind<TeleportMode>(setting6, null); } } else { value = (ConfigEntryBase)(object)Bind<SortOrder>(setting5, null); } } else { value = (ConfigEntryBase)(object)Bind<HubEditing>(setting4, null); } } else { value = (ConfigEntryBase)(object)Bind<ReturnToHubMode>(setting3, null); } } else { value = (ConfigEntryBase)(object)Bind<float>(val3, (AcceptableValueBase?)(object)new AcceptableValueRange<float>((float)((ThresholdSetting)val3).Minimum, (float)((ThresholdSetting)val3).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<int>(val2, (AcceptableValueBase?)(object)new AcceptableValueRange<int>((int)((ThresholdSetting)val2).Minimum, (int)((ThresholdSetting)val2).Maximum)); } } else { value = (ConfigEntryBase)(object)Bind<bool>(setting2, null); } } else if (((ThresholdSetting)val).IsShortcut) { value = (ConfigEntryBase)(object)MeadworksConfig.Bind<KeyboardShortcut>(file, ((ThresholdSetting)val).Section, ((ThresholdSetting)val).Key, KeyboardShortcut.Deserialize(val.Default), ((ThresholdSetting)val).Description, (ConfigScope)0, (AcceptableValueBase)null); } else { ThresholdSetting<string> setting9 = val; value = (ConfigEntryBase)(object)Bind<string>(setting9, null); } entries.Add(setting, value); } private ConfigDescription Describe(ThresholdSetting setting, AcceptableValueBase? acceptable, string? text = null) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown ConfigDependency val = null; if ((object)setting == Fuel.FuelItem || (object)setting == Fuel.FuelAmount) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Fuel.FuelEnabled)), (Func<string>)(() => "Requires " + ((ThresholdSetting)Fuel.FuelEnabled).Section + ": " + ((ThresholdSetting)Fuel.FuelEnabled).Key + "."), (Func<bool>)null); } if ((object)setting == Tames.TameRadius || (object)setting == Tames.MaxTames) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Tames.BringTames)), (Func<string>)(() => "Requires " + ((ThresholdSetting)Tames.BringTames).Section + ": " + ((ThresholdSetting)Tames.BringTames).Key + "."), (Func<bool>)null); } if ((object)setting == Access.DefaultAccessMode) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Access.AccessModesEnabled)), (Func<string>)(() => "Requires " + ((ThresholdSetting)Access.AccessModesEnabled).Section + ": " + ((ThresholdSetting)Access.AccessModesEnabled).Key + "."), (Func<bool>)null); } if ((object)setting == Jumps.QuickPortalRange || (object)setting == Jumps.QuickPortalSeconds) { val = new ConfigDependency((Func<bool>)(() => Get<bool>(Jumps.QuickPortals)), (Func<string>)(() => "Requires " + ((ThresholdSetting)Jumps.QuickPortals).Section + ": " + ((ThresholdSetting)Jumps.QuickPortals).Key + "."), (Func<bool>)null); } return new ConfigDescription(text ?? setting.Description, acceptable, (val == null) ? Array.Empty<object>() : new object[1] { val }); } private ConfigEntry<T> Bind<T>(ThresholdSetting<T> setting, AcceptableValueBase? acceptable) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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) if ((int)((ThresholdSetting)setting).Scope == 2) { return MeadworksConfig.BindCheat<T>(file, ((ThresholdSetting)setting).Section, ((ThresholdSetting)setting).Key, setting.Safe, ((ThresholdSetting)setting).Description, acceptable); } if (((ThresholdSetting)setting).Hidden) { return MeadworksConfig.BindHidden<T>(file, ((ThresholdSetting)setting).Section, ((ThresholdSetting)setting).Key, setting.Default, ((ThresholdSetting)setting).Description, Map(((ThresholdSetting)setting).Scope), acceptable); } return MeadworksConfig.Bind<T>(file, ((ThresholdSetting)setting).Section, ((ThresholdSetting)setting).Key, setting.Default, Describe((ThresholdSetting)(object)setting, acceptable), Map(((ThresholdSetting)setting).Scope)); } private static int Count(SettingScope scope) { //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) return ThresholdSettings.All.Count((ThresholdSetting s) => s.Scope == scope && !s.Hidden); } } public sealed class ThresholdPlayerLocks : IDisposable { private sealed class SyncReceipt { private ZNet? network; private ZRpc? server; private string epoch = ""; private long world; internal long CurrentGeneration { get { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 if (!Plugin.IsReady || (Object)(object)network == (Object)null || server == null || !server.IsConnected() || network != ZNet.instance || network.IsServer() || server != network.GetServerRPC()) { return -1L; } SessionSnapshot session = Plugin.Session; if ((int)session.Role != 3 || !session.AuthorityReady || session.WorldId == 0L || string.IsNullOrEmpty(session.SessionId)) { return -1L; } if (epoch.Length == 0) { epoch = session.SessionId; world = session.WorldId; } if (!(epoch == session.SessionId) || world != session.WorldId) { return -1L; } return session.Generation; } } internal void Observe() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 network = ZNet.instance; server = (((Object)(object)network != (Object)null && !network.IsServer()) ? network.GetServerRPC() : null); epoch = ""; world = 0L; if (server != null && server.IsConnected() && Plugin.IsReady) { SessionSnapshot session = Plugin.Session; if ((int)session.Role == 3 && !string.IsNullOrEmpty(session.SessionId)) { epoch = session.SessionId; world = session.WorldId; } } } } private const string Product = "com.meadworks.threshold"; private const int MaxText = 1048576; private readonly ConfigFile file; private readonly MeadworksLog log; private readonly ConfigEntry<string> listEntry; private readonly ConfigEntry<string> carrier; private readonly Dictionary<string, ConfigEntryBase> locals; private readonly HashSet<ConfigEntryBase> tracked = new HashSet<ConfigEntryBase>(); private readonly SyncReceipt receipt = new SyncReceipt(); private IReadOnlyList<string> locks = Array.Empty<string>(); private IReadOnlyDictionary<string, string> values = new Dictionary<string, string>(); private string listText = ""; private string valuesText = ""; private float next; private bool disposed; private bool refreshing; private bool dirty = true; public ThresholdPlayerLocks(ConfigFile file, ConfigEntry<string> listEntry, Dictionary<string, ConfigEntryBase> locals, MeadworksLog log) { this.file = file; this.listEntry = listEntry; this.locals = locals; this.log = log; carrier = MeadworksConfig.BindHidden<string>(file, "1 - General", "Locked Player Values", "", "Managed serialized server values for Locked Player Settings; do not edit.", (ConfigScope)1, (AcceptableValueBase)null); file.SettingChanged += OnChanged; SynchronizationManager.OnConfigurationSynchronized += OnSynchronized; Refresh(); } private void OnChanged(object sender, SettingChangedEventArgs args) { dirty = true; } private void OnSynchronized(object sender, ConfigurationSynchronizationEventArgs args) { if (args.UpdatedPluginGUIDs != null && args.UpdatedPluginGUIDs.Contains("com.meadworks.threshold") && Plugin.IsReady) { receipt.Observe(); dirty = true; Refresh(); } } private bool ServerBinds() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 if (Plugin.IsReady && (int)Plugin.Session.Role == 3 && receipt.CurrentGeneration == Plugin.Session.Generation) { return (int)MeadworksAuthority.Decide("com.meadworks.threshold", (ConfigScope)1, true).Source == 1; } return false; } public void Tick() { if (!disposed) { float unscaledTime = Time.unscaledTime; if (dirty || !(unscaledTime < next)) { next = unscaledTime + 0.5f; Refresh(); } } } private void Refresh() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 if (disposed || refreshing) { return; } refreshing = true; try { dirty = false; if (Plugin.IsReady && (int)Plugin.Session.Role != 3) { ConfigEntryBase value; string text = LockedPlayerValues.Compose(listEntry.Value, (Func<string, string>)((string id) => (!locals.TryGetValue(id, out value)) ? null : value.GetSerializedValue())); if (text != carrier.Value) { MeadworksConfig.Amend<string>(carrier, "com.meadworks.threshold", (ConfigScope)1, text, (string)null, true); } } string text2 = MeadworksConfig.Effective<string>(listEntry, "com.meadworks.threshold", (ConfigScope)1, "", true); if (text2 != listText) { listText = text2; IReadOnlyList<string> readOnlyList2; if (text2.Length > 1048576) { IReadOnlyList<string> readOnlyList = Array.Empty<string>(); readOnlyList2 = readOnlyList; } else { readOnlyList2 = ThresholdSettings.ParseLocked(text2); } locks = readOnlyList2; int num = ThresholdSettings.NameList(text2).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries).Length; if (num > locks.Count) { log.Warning("Locked Player Settings: " + (num - locks.Count) + " unknown or non-player entries ignored."); } } string text3 = MeadworksConfig.Effective<string>(carrier, "com.meadworks.threshold", (ConfigScope)1, "", true); if (text3 != valuesText) { valuesText = text3; IReadOnlyDictionary<string, string> readOnlyDictionary2; if (text3.Length > 1048576) { IReadOnlyDictionary<string, string> readOnlyDictionary = new Dictionary<string, string>(); readOnlyDictionary2 = readOnlyDictionary; } else { readOnlyDictionary2 = LockedPlayerValues.Decode(text3); } values = readOnlyDictionary2; } bool flag = ServerBinds(); foreach (KeyValuePair<string, ConfigEntryBase> local in locals) { bool flag2 = LockedPlayerValues.ServerText(local.Key, flag, locks, values) != null; if (flag2 && tracked.Add(local.Value)) { MeadworksBlockedOptions.Untrack(local.Value); MeadworksBlockedOptions.Track("com.meadworks.threshold", local.Value, (ConfigScope)1, true); } else if (!flag2 && tracked.Remove(local.Value)) { MeadworksBlockedOptions.Untrack(local.Value); } } } catch (Exception ex) { log.Warning("Player setting lock refresh: " + ex.GetType().Name + ": " + ex.Message); } finally { refreshing = false; } } public T Effective<T>(ConfigEntry<T> entry, T own) { if (disposed) { return own; } if (dirty) { Refresh(); } string text = LockedPlayerValues.ServerText(((ConfigEntryBase)entry).Definition.Section + "." + ((ConfigEntryBase)entry).Definition.Key, ServerBinds(), locks, values); if (text == null) { return own; } try { object obj = TomlTypeConverter.ConvertToValue(text, ((ConfigEntryBase)entry).SettingType); if (((ConfigEntryBase)entry).Description.AcceptableValues != null && !((ConfigEntryBase)entry).Description.AcceptableValues.IsValid(obj)) { return (T)((ConfigEntryBase)entry).DefaultValue; } return (T)obj; } catch (Exception) { return (T)((ConfigEntryBase)entry).DefaultValue; } } public void Dispose() { if (disposed) { return; } disposed = true; file.SettingChanged -= OnChanged; SynchronizationManager.OnConfigurationSynchronized -= OnSynchronized; foreach (ConfigEntryBase item in tracked) { MeadworksBlockedOptions.Untrack(item); } tracked.Clear(); } } public static class ThresholdModules { private static StepAsideDecision? stepAside; private static long noticedSession = -1L; private static long offNoticedSession = -1L; private static bool teleportEverything; private static long teNoticedSession = -1L; public static bool SteppedAside { get { StepAsideDecision? obj = stepAside; if (obj == null) { return false; } return obj.SteppedAside; } } public static TeleportMode Mode => (TeleportMode)(((??)Plugin.Settings?.Get<TeleportMode>(General.TeleportMode)) ?? 0); public static void Decide(MeadworksLog log) { if (!Plugin.Services.StepAside.Declared("com.meadworks.threshold").Any()) { MeadworksStepAside.Declare("com.meadworks.threshold", "Threshold", ((IEnumerable<SourcePlugin>)ProductStepAside.Sources).Select((Func<SourcePlugin, SourceMod>)((SourcePlugin s) => new SourceMod(s.Guid, s.Name)))); } MeadworksStepAside.ShouldYield("com.meadworks.threshold"); stepAside = ProductStepAside.Decide((IEnumerable<string>)Chainloader.PluginInfos.Keys); if (stepAside.SteppedAside) { log.Info("Threshold steps aside for " + stepAside.SourceNames + "."); } teleportEverything = TeleportEverythingStepAside.Installed((IEnumerable<string>)Chainloader.PluginInfos.Keys); if (teleportEverything) { log.Info("Threshold leaves Teleport Listed Items and Bring Tames to TeleportEverything."); } } public static bool ProductActive(out ConfigBlockReason reason) { reason = (ConfigBlockReason)0; if (Plugin.Ready) { return MeadworksAuthority.ProductActive("com.meadworks.threshold", ref reason); } return false; } public static bool Active() { ThresholdConfig settings = Plugin.Settings; ConfigBlockReason reason; if (Plugin.Ready && settings != null && !SteppedAside && settings.Get<bool>(General.Enabled)) { return ProductActive(out reason); } return false; } public static bool ExtrasActive() { return TeleportEverythingStepAside.ExtrasActive(teleportEverything, Active()); } public static bool PortalRoutingActive() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (Active()) { return PortalModeRules.Routes(Mode); } return false; } public static bool ServerActive() { return PortalRoutingActive(); } public static bool DiscoveryEnabled() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) ThresholdConfig settings = Plugin.Settings; if (Plugin.Ready && settings != null && stepAside != null && !SteppedAside && settings.Get<bool>(General.Enabled)) { return PortalModeRules.Routes(Mode); } return false; } public static void Tick(MeadworksLog log) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Invalid comparison between Unknown and I4 //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Invalid comparison between Unknown and I4 if (Plugin.Ready && !((Object)(object)Player.m_localPlayer == (Object)null)) { SessionSnapshot session = Plugin.Session; if (stepAside != null && stepAside.SteppedAside && noticedSession != session.Generation) { noticedSession = session.Generation; ThresholdText.Centre(ThresholdText.Format("step_aside", stepAside.SourceNames), log); } ThresholdConfig settings = Plugin.Settings; if (teleportEverything && teNoticedSession != session.Generation && settings != null && Active() && (settings.Get<bool>(Tames.BringTames) || settings.Get<bool>(Cheats.TeleportListedItems))) { teNoticedSession = session.Generation; ThresholdText.Centre(ThresholdText.Text("te_step_aside"), log); } if (offNoticedSession != session.Generation && session.AuthorityReady && !ProductActive(out var reason) && ((int)reason == 4 || (int)reason == 5)) { offNoticedSession = session.Generation; string text = ThresholdText.Text(((int)reason == 4) ? "off_no_crucible" : "off_no_threshold"); ThresholdText.Centre(text, log); log.Info(text); } } } public static void Reset() { stepAside = null; noticedSession = -1L; offNoticedSession = -1L; teleportEverything = false; teNoticedSession = -1L; } } public static class ThresholdText { private static MeadworksTranslations? catalog; public static void Initialize(BepInPlugin metadata, ManualLogSource log) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Assembly assembly = typeof(ThresholdText).Assembly; foreach (string item in MeadworksLanguages.Supported) { using Stream stream = assembly.GetManifestResourceStream("Threshold.localization." + item + ".json"); if (stream != null) { using StreamReader streamReader = new StreamReader(stream); dictionary.Add(item, streamReader.ReadToEnd()); } } catalog = MeadworksTranslations.Register(metadata, new ProductTranslations("threshold", (IReadOnlyDictionary<string, string>)dictionary), (string)null, log); } public static string Text(string key) { MeadworksTranslations? obj = catalog; return ((obj != null) ? obj.Text(key) : null) ?? key; } public static string Format(string key, params object[] arguments) { MeadworksTranslations? obj = catalog; return ((obj != null) ? obj.Format(key, arguments) : null) ?? key; } public static void Tick() { MeadworksTranslations? obj = catalog; if (obj != null) { obj.Update(); } } public static void Shutdown() { MeadworksTranslations? obj = catalog; if (obj != null) { obj.Dispose(); } catalog = null; } public static void Centre(string text, MeadworksLog log) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if (MeadworksHeadless.AllowsUi && (Object)(object)MessageHud.instance != (Object)null) { ProductRegistration source = Plugin.Source; if (source != null && source.IsActive && CrucibleUiPlugin.Active) { KitNotices.Show(source, new KitNotice(text, (KitNoticeKind)1, "", 0, (Sprite)null, true, false, true)); } else { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } } else { log.Info(text); } } } [BepInPlugin("com.meadworks.threshold", "Threshold", "0.3.10")] [BepInDependency("com.meadworks.crucible", "0.3.26")] [BepInDependency("com.meadworks.crucible.ui", "0.3.19")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "com.meadworks.threshold"; public const string Name = "Threshold"; public const string Version = "0.3.10"; public const string Family = "Waystone"; private ProductRegistration? registration; private MeadworksLog? log; private ThresholdSourceConversion? conversion; private ThresholdSourceConversion? wayfareConversion; private Harmony? harmony; private bool started; internal static ProductRegistration? Source { get { if (!Chainloader.PluginInfos.TryGetValue("com.meadworks.threshold", out var value)) { return null; } return (value.Instance as Plugin)?.registration; } } public static bool Ready { get; private set; } public static bool AllowsUi { get; private set; } internal static MeadworksLog? Log { get; private set; } public static ThresholdConfig? Settings { get; private set; } private void Awake() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown log = MeadworksLog.For(((BaseUnityPlugin)this).Logger, "Threshold"); Log = log; try { if (!Plugin.IsReady) { throw new InvalidOperationException("Required Crucible is not ready"); } registration = Plugin.Services.Register(new ProductMetadata("com.meadworks.threshold", "Threshold", new Version("0.3.10"), new Version(0, 1), new Version(0, 1), (MultiplayerPolicy)2, "Waystone")); AllowsUi = MeadworksHeadless.AllowsUi; Settings = ThresholdConfig.Bind(((BaseUnityPlugin)this).Config, log); ThresholdText.Initialize(((BaseUnityPlugin)this).Info.Metadata, ((BaseUnityPlugin)this).Logger); conversion = new ThresholdSourceConversion(((BaseUnityPlugin)this).Config, Settings, log); wayfareConversion = new ThresholdSourceConversion(((BaseUnityPlugin)this).Config, Settings, log, wayfare: true); ThresholdPortals.Initialize(registration); ThresholdPortalEdits.Initialize(registration); Ready = true; log.Info(AllowsUi ? "Threshold registered; step-aside is decided after plugin discovery." : "Threshold registered on a headless process; no UI is created."); } catch (Exception ex) { log.Error((object)("Threshold initialization rejected: " + ex.GetType().Name + ": " + ex.Message + ((ex.InnerException != null) ? (" <- " + ex.GetBaseException().GetType().Name + ": " + ex.GetBaseException().Message + " at " + (ex.GetBaseException().StackTrace ?? "").Replace(Environment.NewLine, " | ")) : ""))); Shutdown(); } } private void Start() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown if (!Ready || registration == null || log == null) { return; } try { ThresholdModules.Decide(log); if (!ThresholdModules.SteppedAside) { harmony = new Harmony("com.meadworks.threshold"); ThresholdPortalDiscovery.Patch(harmony); ThresholdTravel.Patch(harmony); ThresholdPortalEdits.Patch(harmony); ExtraPotions.Threshold.Runtime.SeaGates.SeaGates.Patch(harmony); if (AllowsUi) { ThresholdJumps.Patch(harmony); ThresholdPickerWindow.Patch(harmony); ThresholdPickerWindow.Install(); } } started = true; } catch (Exception ex) { log.Error((object)("Threshold start rejected: " + ex.GetType().Name + ": " + ex.Message)); Shutdown(); } } private void Update() { if (!Ready) { return; } ThresholdText.Tick(); Settings?.Tick(); if (started) { if (log != null) { ThresholdModules.Tick(log); } ThresholdPortalDiscovery.Tick(ThresholdModules.DiscoveryEnabled()); ThresholdPortals.Tick(ThresholdModules.PortalRoutingActive()); ThresholdPortalEdits.Tick(); if (!ThresholdModules.SteppedAside) { ExtraPotions.Threshold.Runtime.SeaGates.SeaGates.Tick(); } if (AllowsUi) { ThresholdTravel.OpenPending(); ThresholdPickerWindow.Tick(); ThresholdTargetPicker.Tick(); ThresholdSeaGatePicker.Tick(); ThresholdTames.Tick(); } } } private void LateUpdate() { if (Ready && started && AllowsUi) { ThresholdMapPins.LateTick(); } } private void OnDestroy() { Shutdown(); } private void Shutdown() { Ready = false; started = false; wayfareConversion?.Dispose(); wayfareConversion = null; conversion?.Dispose(); conversion = null; Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; Action[] array = new Action[12] { ThresholdMapPins.Shutdown, ThresholdPickerWindow.Shutdown, ThresholdTargetPicker.Shutdown, ThresholdSeaGatePicker.Shutdown, ExtraPotions.Threshold.Runtime.SeaGates.SeaGates.Reset, ThresholdJumps.Reset, ThresholdTravel.Reset, ThresholdPortalEdits.Shutdown, ThresholdPortals.Shutdown, ThresholdPortalDiscovery.Reset, ThresholdModules.Reset, ThresholdText.Shutdown }; foreach (Action action in array) { try { action(); } catch (Exception ex) { MeadworksLog? obj2 = log; if (obj2 != null) { obj2.Warning("Threshold teardown: " + ex.GetType().Name); } } } Settings?.Shutdown(); Settings = null; ProductRegistration? obj3 = registration; if (obj3 != null) { obj3.Dispose(); } registration = null; } } } namespace ExtraPotions.Threshold.Runtime.Travel { public static class ThresholdAreaLoading { private const double LandBudgetMs = 20.0; private const double ObjectBudgetMs = 15.0; private const float QuietSeconds = 0.5f; private const float CheckSeconds = 0.1f; private static bool patched; private static Func<ZoneSystem, Vector3, bool>? createLocalZones; private static FieldInfo? listed; private static Func<ZNetScene, ZDO, GameObject>? create; private static Comparison<ZDO>? order; private static readonly Stopwatch clock = new Stopwatch(); private static readonly List<ZDO> pending = new List<ZDO>(); private static readonly List<ZDO> found = new List<ZDO>(); private static readonly SimulationDistance around = new SimulationDistance(1, 0, false); private static Vector2s zone; private static int count = -1; private static long ids; private static long revisions; private static float quietSince; private static float nextCheck; private static bool settled; private static bool Busy { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsDedicated() || (int)ZNet.GetConnectionStatus() != 2) { return false; } try { return ThresholdJumps.LoadingBehindScreen(); } catch (Exception) { return false; } } } public static void Patch(Harmony harmony) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown if (!patched) { patched = true; createLocalZones = AccessTools.MethodDelegate<Func<ZoneSystem, Vector3, bool>>(AccessTools.Method(typeof(ZoneSystem), "CreateLocalZones", (Type[])null, (Type[])null), (object)null, true); listed = AccessTools.Field(typeof(ZNetScene), "m_tempCurrentObjects"); create = AccessTools.MethodDelegate<Func<ZNetScene, ZDO, GameObject>>(AccessTools.Method(typeof(ZNetScene), "CreateObject", (Type[])null, (Type[])null), (object)null, true); order = AccessTools.MethodDelegate<Comparison<ZDO>>(AccessTools.Method(typeof(ZNetScene), "ZDOCompare", (Type[])null, (Type[])null), (object)null, true); harmony.Patch((MethodBase)AccessTools.Method(typeof(ZoneSystem), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ThresholdAreaLoading), "AfterZones", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "CreateDestroyObjects", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ThresholdAreaLoading), "AfterObjects", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void AfterZones(ZoneSystem __instance) { //IL_0031: 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_0064: Unknown result type (might be due to invalid IL or missing references) if (createLocalZones == null || !Busy || (ZNet.instance.IsServer() && !__instance.LocationsGenerated) || __instance.IsActiveAreaLoaded()) { return; } try { Vector3 referencePosition = ZNet.instance.GetReferencePosition(); clock.Restart(); while (clock.Elapsed.TotalMilliseconds < 20.0 && createLocalZones(__instance, referencePosition)) { } } catch (Exception e) { Off(e); } } private static void AfterObjects(ZNetScene __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (listed == null || create == null || order == null || !Busy || (Object)(object)ZoneSystem.instance == (Object)null) { return; } try { if (!(listed.GetValue(__instance) is List<ZDO> near)) { return; } Collect(near, ZNet.instance.GetReferencePosition()); pending.Sort(order); clock.Restart(); foreach (ZDO item in pending) { if (!(clock.Elapsed.TotalMilliseconds >= 15.0)) { create(__instance, item); continue; } break; } } catch (Exception e) { Off(e); } finally { pending.Clear(); } } private static void Collect(List<ZDO> near, Vector3 at) { //IL_002a: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) pending.Clear(); ZoneSystem instance = ZoneSystem.instance; foreach (ZDO item in near) { if (!item.Created) { Vector2s sector = item.GetSector(); if (instance.IsZoneLoaded(sector) && instance.IsZoneReadyForType(sector, item.Type)) { item.m_tempSortValue = Utils.DistanceSqr(at, item.GetPosition()); pending.Add(item); } } } } public static bool Settled(Vector3 point) { //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_0028: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || ZDOMan.instance == null) { return true; } Vector2s val = ZoneSystem.GetZone(point); if (val == zone && Time.time < nextCheck) { return settled; } nextCheck = Time.time + 0.1f; settled = Look(val); return settled; } private static bool Look(Vector2s at) { //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_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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) found.Clear(); ZDOMan.instance.FindSectorObjects(at, around, found, (List<ZDO>)null); long num = 0L; long num2 = 0L; foreach (ZDO item in found) { num ^= ((ZDOID)(ref item.m_uid)).UserID * 31 + ((ZDOID)(ref item.m_uid)).ID; num2 += item.DataRevision; } int num3 = found.Count; found.Clear(); if (!(at == zone) || num3 != count || num != ids || num2 != revisions) { zone = at; count = num3; ids = num; revisions = num2; quietSince = Time.time; return false; } return Time.time - quietSince >= 0.5f; } private static void Off(Exception e) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Threshold: Quick Area Loading is off (the game's loading members changed): " + e.GetType().Name + ": " + e.Message)); } createLocalZones = null; create = null; } public static void Reset() { patched = false; count = -1; settled = false; nextCheck = 0f; } } public static class ThresholdArrival { public const string SafeKeyName = "threshold.arrival.safe"; public static readonly int SafeKey = StringExtensionMethods.GetStableHashCode("threshold.arrival.safe"); private static readonly FieldRef<Character, ZNetView> View = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview"); private static readonly FieldRef<MonsterAI, Character> TargetCreature = AccessTools.FieldRefAccess<MonsterAI, Character>("m_targetCreature"); private static bool pending; public static bool Pending => pending; public static void Patch(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_005b: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00b7: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(TeleportWorld), "Teleport", new Type[1] { typeof(Player) }, (Type[])null), new HarmonyMethod(typeof(ThresholdArrival), "BeforePortal", (Type[])null), new HarmonyMethod(typeof(ThresholdArrival), "AfterPortal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "UpdateTeleport", new Type[1] { typeof(float) }, (Type[])null), new HarmonyMethod(typeof(ThresholdArrival), "BeforeStep", (Type[])null), new HarmonyMethod(typeof(ThresholdArrival), "AfterStep", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(MonsterAI), "UpdateTarget", new Type[4] { typeof(Humanoid), typeof(float), typeof(bool).MakeByRefType(), typeof(bool).MakeByRefType() }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ThresholdArrival), "AfterUpdateTarget", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static void Begin(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { pending = true; } } private static void BeforePortal(Player player, out bool __state) { __state = (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsTeleporting(); } private static void AfterPortal(Player player, bool __state) { if (__state && (Object)(object)player != (Object)null && ((Character)player).IsTeleporting()) { Begin(player); } } private static void BeforeStep(Player __instance, out bool __state) { __state = false; if (pending && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { if (((Character)__instance).IsTeleporting()) { __state = true; } else { pending = false; } } } private static void AfterStep(Player __instance, bool __state) { if (!__state || (Object)(object)__instance == (Object)null || ((Character)__instance).IsTeleporting()) { return; } pending = false; try { Arrived(__instance); } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Warning("Threshold: arrival protection failed: " + ex.GetType().Name + ": " + ex.Message); } } } private static void Arrived(Player player) { ThresholdConfig settings = Plugin.Settings; if (settings == null || !ThresholdModules.Active()) { return; } float num = settings.Get<float>(Jumps.ArrivalProtectionSeconds); if (!(num <= 0f)) { JumpProtection.Start(num); ZNetView val = View.Invoke((Character)(object)player); if ((Object)(object)val != (Object)null && val.IsValid() && val.IsOwner()) { val.GetZDO().Set(SafeKey, SeaGateFields.After(num)); } MeadworksLog? log = Plugin.Log; if (log != null) { log.Verbose("Threshold: arrival protection for " + num.ToString("0.#") + " s"); } } } public static bool Protected(Character? character) { if (!(character is Player)) { return false; } ZNetView val = View.Invoke(character); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null) { return val2.GetLong(SafeKey, 0L) > SeaGateFields.Now; } return false; } private static void AfterUpdateTarget(MonsterAI __instance) { if (!((Object)(object)__instance == (Object)null)) { ref Character reference = ref TargetCreature.Invoke(__instance); if ((Object)(object)reference != (Object)null && Protected(reference)) { reference = null; } } } public static void Reset() { pending = false; } } public static class ThresholdCarts { private const float GiveUpSeconds = 120f; private static ZDOID pending = ZDOID.None; private static readonly List<(Rigidbody Body, bool Kinematic)> held = new List<(Rigidbody, bool)>(); private static MapPoint exit; private static float yaw; private static float startedAt; public static ZDOID Pending => pending; public static bool Enabled(ThresholdConfig settings) { if (settings != null && ThresholdModules.ExtrasActive()) { return settings.Get<bool>(Tames.BringCarts); } return false; } public static void Patch(Harmony harmony) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(Container), "RPC_RequestOpen", new Type[2] { typeof(long), typeof(long) }, (Type[])null), new HarmonyMethod(typeof(ThresholdCarts), "BeforeRequestOpen", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Vagon), "RPC_RequestOwn", new Type[1] { typeof(long) }, (Type[])null), new HarmonyMethod(typeof(ThresholdCarts), "BeforeRequestOwn", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static Vagon? Pulled(Player player) { if ((Object)(object)player == (Object)null) { return null; } foreach (Vagon instance in Vagon.m_instances) { if ((Object)(object)instance != (Object)null && instance.IsAttached((Character)(object)player)) { return instance; } } return null; } public unsafe static CartOutcome Decide(Vagon? cart, ThresholdConfig settings, bool enteredAllowsAllItems, bool destinationAllowsAllItems, Func<Inventory, bool> teleportable) { //IL_00a0: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (((Object)(object)cart != (Object)null) ? cart.m_nview : null); Container val2 = (((Object)(object)cart != (Object)null) ? cart.m_container : null); Inventory val3 = (((Object)(object)val2 != (Object)null) ? val2.GetInventory() : null); bool num = (Object)(object)cart != (Object)null; bool flag = (Object)(object)val != (Object)null && val.IsValid() && val.IsOwner() && (Object)(object)val2 != (Object)null && val3 != null && !val2.IsInUse(); CartOutcome result = CartRules.Decide(new CartFacts(num, Enabled(settings), flag, flag && !val3.IsTeleportable(true), flag && teleportable(val3), enteredAllowsAllItems, destinationAllowsAllItems, settings.Get<OreTransportMode>(General.OreTransport), settings.IgnoreTeleportRestrictions)); if (num) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Debug("Threshold carts: " + ((object)(*(CartOutcome*)(&result))/*cast due to .constrained prefix*/).ToString() + ((val3 != null) ? (" (" + val3.NrOfItems() + " stacks)") : "")); } } return result; } public unsafe static void Begin(Vagon cart, MapPoint exitPoint, float exitYaw) { //IL_003b: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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) Reset(); ZNetView val = (((Object)(object)cart != (Object)null) ? cart.m_nview : null); if (!((Object)(object)cart == (Object)null) && !((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner()) { exit = exitPoint; yaw = exitYaw; startedAt = Time.time; cart.Detach(); MapPoint val2 = CartRules.Place(exitPoint, exitYaw); Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(((MapPoint)(ref val2)).X, ((MapPoint)(ref val2)).Y, ((MapPoint)(ref val2)).Z); Quaternion val4 = Quaternion.Euler(0f, exitYaw, 0f); Move(cart, val3, val4, hold: true); ZDO zDO = val.GetZDO(); zDO.SetPosition(val3); zDO.SetRotation(val4); pending = zDO.m_uid; MeadworksLog? log = Plugin.Log; if (log != null) { ZDOID val5 = pending; log.Debug("Threshold carts: " + ((object)(*(ZDOID*)(&val5))/*cast due to .constrained prefix*/).ToString() + " sent to " + ((Vector3)(ref val3)).ToString("F1")); } } } public unsafe static void Tick() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_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) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00cc: Unknown result type (might be due to invalid IL or missing references) if (pending == ZDOID.None) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || Time.time - startedAt > 120f) { MeadworksLog? log = Plugin.Log; if (log != null) { ZDOID val = pending; log.Debug("Threshold carts: " + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString() + " left at the destination portal (player gone, dead or the jump timed out)."); } Reset(); } else { if (((Character)localPlayer).IsTeleporting()) { return; } try { Vector3 position = ((Component)localPlayer).transform.position; MapPoint val2 = default(MapPoint); ((MapPoint)(ref val2))..ctor(position.x, position.y, position.z); if (TameRules.Landed(val2, exit)) { SetDown(val2); } else { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { ZDOID val = pending; log2.Debug("Threshold carts: the player is not over the exit; " + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString() + " stays beside the destination portal."); } } } catch (Exception ex) { MeadworksLog? log3 = Plugin.Log; if (log3 != null) { log3.Warning("Threshold carts: set-down failed: " + ex.GetType().Name + ": " + ex.Message); } } Reset(); } } private unsafe static void SetDown(MapPoint landed) { //IL_000d: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012b: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: 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_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(pending) : null); if (val == null || !val.IsValid()) { MeadworksLog? log = Plugin.Log; if (log != null) { ZDOID val2 = pending; log.Debug("Threshold carts: " + ((object)(*(ZDOID*)(&val2))/*cast due to .constrained prefix*/).ToString() + " no longer exists."); } return; } long sessionID = ZDOMan.GetSessionID(); ZNetView val3 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(val) : null); Vagon val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<Vagon>() : null); bool flag = (Object)(object)val4 != (Object)null && (Object)(object)val4.m_container != (Object)null && val4.m_container.IsInUse(); if ((val.HasOwner() && val.GetOwner() != sessionID) || flag) { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { ZDOID val2 = pending; log2.Debug("Threshold carts: " + ((object)(*(ZDOID*)(&val2))/*cast due to .constrained prefix*/).ToString() + " is another player's now or open; left where it is."); } return; } MapPoint val5 = CartRules.Place(landed, yaw); float value = default(float); float? num = (((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(new Vector3(((MapPoint)(ref val5)).X, TameRules.ProbeHeight(landed), ((MapPoint)(ref val5)).Z), ref value)) ? new float?(value) : ((float?)null)); MapPoint val6 = CartRules.Settle(val5, landed, num); Vector3 val7 = default(Vector3); ((Vector3)(ref val7))..ctor(((MapPoint)(ref val6)).X, ((MapPoint)(ref val6)).Y, ((MapPoint)(ref val6)).Z); Quaternion val8 = Quaternion.Euler(0f, yaw, 0f); val.SetOwner(sessionID); val.SetPosition(val7); val.SetRotation(val8); if ((Object)(object)val4 != (Object)null) { Move(val4, val7, val8, hold: false); } MeadworksLog? log3 = Plugin.Log; if (log3 != null) { string[] obj = new string[5] { "Threshold carts: ", null, null, null, null }; ZDOID val2 = pending; obj[1] = ((object)(*(ZDOID*)(&val2))/*cast due to .constrained prefix*/).ToString(); obj[2] = " set down at "; obj[3] = ((Vector3)(ref val7)).ToString("F1"); obj[4] = (((Object)(object)val4 != (Object)null) ? " (loaded)" : " (rebuilt by streaming)"); log3.Debug(string.Concat(obj)); } } private static void Move(Vagon cart, Vector3 to, Quaternion turn, bool hold) { //IL_000f: 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_001b: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)cart).transform; Vector3 from = transform.position; Quaternion undo = Quaternion.Inverse(transform.rotation); List<(Rigidbody Body, Vector3 Offset, Quaternion Turn)> list = (from b in (from b in ((Component)cart).GetComponentsInChildren<Rigidbody>(true) where (Object)(object)b != (Object)null select b).ToList() select (Body: b, Offset: undo * (b.position - @from), Turn: undo * b.rotation)).ToList(); transform.SetPositionAndRotation(to, turn); foreach (var item in list) { var (body, val, val2) = item; if (hold && !held.Any<(Rigidbody, bool)>(((Rigidbody Body, bool Kinematic) h) => (Object)(object)h.Body == (Object)(object)body)) { held.Add((body, body.isKinematic)); } if (!body.isKinematic) { body.linearVelocity = Vector3.zero; body.angularVelocity = Vector3.zero; } ((Component)body).transform.SetPositionAndRotation(to + turn * val, turn * val2); body.position = ((Component)body).transform.position; body.rotation = ((Component)body).transform.rotation; if (hold) { body.isKinematic = true; } } if (!hold) { Release(); } } private static void Release() { foreach (var (val, isKinematic) in held) { if ((Object)(object)val != (Object)null) { val.isKinematic = isKinematic; } } held.Clear(); } private static bool Carried(ZNetView? view) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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 (pending != ZDOID.None && (Object)(object)view != (Object)null && view.IsValid()) { return view.GetZDO().m_uid == pending; } return false; } private static bool BeforeRequestOpen(Container __instance, long __0) { if ((Object)(object)__instance == (Object)null || !Carried(__instance.m_nview)) { return true; } __instance.m_nview.InvokeRPC(__0, "RPC_OpenResponse", new object[1] { false }); return false; } private static bool BeforeRequestOwn(Vagon __instance, long __0) { if ((Object)(object)__instance == (Object)null || !Carried(__instance.m_nview)) { return true; } __instance.m_nview.InvokeRPC(__0, "RPC_RequestDenied", Array.Empty<object>()); return false; } public static string Message(CartOutcome outcome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)outcome != 4) { if ((int)outcome != 3) { return ""; } return ThresholdText.Text("cart_stays"); } return ThresholdText.Text("cart_blocked"); } public static void Reset() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Release(); pending = ZDOID.None; } } public static class ThresholdJumps { private static readonly FieldRef<Player, bool> Teleporting = AccessTools.FieldRefAccess<Player, bool>("m_teleporting"); private static readonly FieldRef<Player, bool> Distant = AccessTools.FieldRefAccess<Player, bool>("m_distantTeleport"); private static readonly FieldRef<Player, float> Timer = AccessTools.FieldRefAccess<Player, float>("m_teleportTimer"); private static readonly FieldRef<Player, Vector3> From = AccessTools.FieldRefAccess<Player, Vector3>("m_teleportFromPos"); private static readonly FieldRef<Player, Vector3> Target = AccessTools.FieldRefAccess<Player, Vector3>("m_teleportTargetPos"); private static readonly FieldRef<Hud, bool> HaveSetupLoadScreen = AccessTools.FieldRefAccess<Hud, bool>("m_haveSetupLoadScreen"); private const float FadeSeconds = 1f; private const float FastFadeSeconds = 0.2f; private static bool ours; private static bool clearScreen; private static float seconds = 8f; private static float startedAt; private static bool failed; public static void Patch(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00d6: 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_00f8: Expected O, but got Unknown //IL_00f8: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "UpdateTeleport", new Type[1] { typeof(float) }, (Type[])null), new HarmonyMethod(typeof(ThresholdJumps), "BeforeStep", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(Hud), "UpdateBlackScreen", new Type[2] { typeof(Player), typeof(float) }, (Type[])null), new HarmonyMethod(typeof(ThresholdJumps), "BeforeScreen", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(TeleportWorld), "Teleport", new Type[1] { typeof(Player) }, (Type[])null), new HarmonyMethod(typeof(ThresholdJumps), "BeforeNativePortal", (Type[])null), new HarmonyMethod(typeof(ThresholdJumps), "AfterNativePortal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ThresholdAreaLoading.Patch(harmony); } private static void BeforeNativePortal(Player player, out bool __state) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 __state = (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsTeleporting() && ThresholdModules.Active() && (int)ThresholdModules.Mode == 2; } private static void AfterNativePortal(Player player, bool __state) { if (__state && (Object)(object)player != (Object)null && ((Character)player).IsTeleporting()) { Started(player); } } public static void Started(Player player) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_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_00d5: Unknown result type (might be due to invalid IL or missing references) ours = false; ThresholdConfig settings = Plugin.Settings; if (settings == null || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || failed) { return; } try { if (Teleporting.Invoke(player) && Distant.Invoke(player)) { Vector3 val = Target.Invoke(player); float num = JumpRules.MapDistance(Point(From.Invoke(player)), Point(val)); seconds = (settings.Get<bool>(Jumps.QuickPortals) ? JumpRules.Seconds(num, settings.Get<float>(Jumps.QuickPortalRange), settings.Get<float>(Jumps.QuickPortalSeconds)) : 8f); clearScreen = settings.Get<bool>(Jumps.ScreenOnlyWhenLoading) && (Object)(object)ZNetScene.instance != (Object)null && ZNetScene.instance.IsAreaReady(val); startedAt = Time.time; ours = true; MeadworksLog? log = Plugin.Log; if (log != null) { log.Verbose("Threshold: jump of " + num.ToString("F0") + " m takes " + seconds.ToString("0.#") + " s" + (clearScreen ? ", target loaded already (no teleport screen)" : "")); } } } catch (Exception e) { Fail(e); } } public static bool Running(Player? player) { if (ours && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { return ((Character)player).IsTeleporting(); } return false; } public static bool LoadingBehindScreen() { Player localPlayer = Player.m_localPlayer; ThresholdConfig settings = Plugin.Settings; if (Running(localPlayer) && !clearScreen && settings != null) { return settings.Get<bool>(Jumps.QuickAreaLoading); } return false; } private static bool BeforeStep(Player __instance, float dt) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (CrewHold.Holding) { return !CrewHold.Step(__instance, dt); } CrewHold.Between(__instance); if (!ours) { return true; } try { if (!Teleporting.Invoke(__instance) || !ThresholdModules.Active()) { ours = false; return true; } if (!Distant.Invoke(__instance) || seconds >= 8f) { return true; } ref float reference = ref Timer.Invoke(__instance); reference = JumpRules.Step(reference, dt, seconds); reference = JumpRules.HoldLanding(reference, dt, Time.time - startedAt, ThresholdAreaLoading.Settled(Target.Invoke(__instance))); } catch (Exception e) { Fail(e); } return true; } private static bool BeforeScreen(Hud __instance, Player player, float dt) { if (!ours || (Object)(object)__instance == (Object)null || !Local(player)) { return true; } try { if (clearScreen && ((Character)player).IsTeleporting()) { HaveSetupLoadScreen.Invoke(__instance) = false; __instance.m_loadingScreen.alpha = Mathf.MoveTowards(__instance.m_loadingScreen.alpha, 0f, dt / 1f); if (__instance.m_loadingScreen.alpha <= 0f) { ((Component)__instance.m_loadingScreen).gameObject.SetActive(false); } return false; } if (!clearScreen && seconds < 8f && player.ShowTeleportAnimation()) { ((Component)__instance.m_loadingScreen).gameObject.SetActive(true); __instance.m_loadingScreen.alpha = Mathf.MoveTowards(__instance.m_loadingScreen.alpha, 1f, dt / 0.2f); } } catch (Exception e) { Fail(e); } return true; } private static bool Local(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead()) { return !player.IsSleeping(); } return false; } private static MapPoint Point(Vector3 v) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new MapPoint(v.x, v.y, v.z); } private static void Fail(Exception e) { ours = false; if (!failed) { failed = true; MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Threshold: quick jumps are off (the game's jump members changed): " + e.GetType().Name + ": " + e.Message)); } } } public static void Reset() { ours = false; clearScreen = false; seconds = 8f; ThresholdAreaLoading.Reset(); } } public static class ThresholdTames { private const float GiveUpSeconds = 120f; private static readonly List<ZDOID> pending = new List<ZDOID>(); private static MapPoint exit; private static float yaw; private static float startedAt; public static bool Enabled(ThresholdConfig settings) { if (settings != null && ThresholdModules.ExtrasActive()) { return settings.Get<bool>(Tames.BringTames); } return false; } public unsafe static List<ZDOID> Choose(Player player, ThresholdConfig settings) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) List<ZDOID> list = new List<ZDOID>(); if ((Object)(object)player == (Object)null || !Enabled(settings)) { return list; } List<ZNetView> views = new List<ZNetView>(); List<TameCandidate> list2 = new List<TameCandidate>(); foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)player || allCharacter.IsPlayer()) { continue; } ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.GetFollowTarget() == (Object)(object)((Component)player).gameObject) { Vector3 position = ((Component)allCharacter).transform.position; list2.Add(new TameCandidate(views.Count, new MapPoint(position.x, position.y, position.z), allCharacter.IsTamed(), true, allCharacter.IsDead())); views.Add(component); } } } Vector3 position2 = ((Component)player).transform.position; IReadOnlyList<int> readOnlyList = TameRules.Select((IEnumerable<TameCandidate>)list2, new MapPoint(position2.x, position2.y, position2.z), settings.Get<float>(Tames.TameRadius), settings.Get<int>(Tames.MaxTames)); foreach (int item in readOnlyList) { list.Add(views[item].GetZDO().m_uid); } MeadworksLog? log = Plugin.Log; if (log != null) { log.Debug("Threshold tames: " + list2.Count + " following, " + list.Count + " chosen within " + settings.Get<float>(Tames.TameRadius).ToString("0.#") + " m" + ((list.Count > 0) ? (": " + string.Join(", ", readOnlyList.Select(delegate(int i) { //IL_002c: 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) string prefabName = Utils.GetPrefabName(((Component)views[i]).gameObject); ZDOID uid = views[i].GetZDO().m_uid; return prefabName + " " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); }))) : "")); } return list; } public static void Begin(List<ZDOID> tames, MapPoint exitPoint, float exitYaw) { //IL_0021: 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) pending.Clear(); if (tames != null && tames.Count != 0) { pending.AddRange(tames); exit = exitPoint; yaw = exitYaw; startedAt = Time.time; } } public static void Tick() { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) ThresholdCarts.Tick(); if (pending.Count == 0) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || Time.time - startedAt > 120f) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Debug("Threshold tames: " + pending.Count + " left behind (player gone, dead or the jump timed out)."); } pending.Clear(); } else { if (((Character)localPlayer).IsTeleporting()) { return; } Vector3 position = ((Component)localPlayer).transform.position; if (!TameRules.Landed(new MapPoint(position.x, position.y, position.z), exit)) { MeadworksLog? log2 = Plugin.Log; if (log2 != null) { log2.Debug("Threshold tames: the player is not over the exit after the jump; " + pending.Count + " stay where they are."); } pending.Clear(); return; } try { Move(new MapPoint(position.x, position.y, position.z)); } catch (Exception ex) { MeadworksLog? log3 = Plugin.Log; if (log3 != null) { log3.Warning("Threshold tames: move failed: " + ex.GetType().Name + ": " + ex.Message); } } pending.Clear(); } } private unsafe static void Move(MapPoint landed) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; ZNetScene instance2 = ZNetScene.instance; if (instance == null || (Object)(object)instance2 == (Object)null) { return; } long sessionID = ZDOMan.GetSessionID(); int num = 0; float value = default(float); Vector3 position = default(Vector3); for (int i = 0; i < pending.Count; i++) { ZDO zDO = instance.GetZDO(pending[i]); if (zDO == null || !zDO.IsValid()) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Debug("Threshold tames: " + ((object)pending[i]/*cast due to .constrained prefix*/).ToString() + " no longer exists."); } continue; } MapPoint val = TameRules.Place(landed, yaw, i, pending.Count); float? num2 = (((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(new Vector3(((MapPoint)(ref val)).X, TameRules.ProbeHeight(landed), ((MapPoint)(ref val)).Z), ref value)) ? new float?(value) : ((float?)null)); MapPoint val2 = TameRules.Settle(val, landed, num2); ((Vector3)(ref position))..ctor(((MapPoint)(ref val2)).X, ((MapPoint)(ref val2)).Y, ((MapPoint)(ref val2)).Z); zDO.SetOwner(sessionID); zDO.SetPosition(position); ZNetView val3 = instance2.FindInstance(zDO); if ((Object)(object)val3 != (Object)null) { ((Component)val3).transform.position = position; Rigidbody component = ((Component)val3).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position = position; component.linearVelocity = Vector3.zero; } } num++; MeadworksLog? log2 = Plugin.Log; if (log2 != null) { string[] obj = new string[6] { "Threshold tames: ", null, null, null, null, null }; ZDOID uid = zDO.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " moved to "; obj[3] = ((Vector3)(ref position)).ToString("F1"); obj[4] = (((Object)(object)val3 != (Object)null) ? " (loaded)" : " (rebuilt by streaming)"); obj[5] = ((num2.HasValue && Mathf.Approximately(((MapPoint)(ref val2)).Y, num2.Value)) ? ", on the floor" : ", at the player (no floor at its spot)"); log2.Debug(string.Concat(obj)); } } MeadworksLog? log3 = Plugin.Log; if (log3 != null) { log3.Debug("Threshold tames: " + num + " of " + pending.Count + " brought."); } } public static void Reset() { pending.Clear(); } } public static class ThresholdTravel { private static readonly FieldRef<TeleportWorld, ZNetView> View = AccessTools.FieldRefAccess<TeleportWorld, ZNetView>("m_nview"); private static readonly int ModeHash = StringExtensionMethods.GetStableHashCode("threshold.mode"); private static readonly int OwnerHash = StringExtensionMethods.GetStableHashCode("threshold.owner"); private static readonly int HubHash = StringExtensionMethods.GetStableHashCode("threshold.hub"); private static readonly DeferredOpen<TeleportWorld> walkIn = new DeferredOpen<TeleportWorld>(); private static bool vanillaJump; private static TeleportMode sessionMode; public static Func<TeleportWorld, bool>? Host { get; set; } public static Func<bool>? CanHost { get; set; } public static Action? CloseHost { get; set; } public static TeleportWorld? Source { get; private set; } public static bool InSession { get { if ((Object)(object)Source != (Object)null) { return SourceZdo(Source) != null; } return false; } } public static PortalEntry? SourceEntry { get { if (!((Object)(object)Source != (Object)null)) { return null; } return EntryOf(Source); } } public static void Patch(Harmony harmony) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown harmony.Patch((MethodBase)AccessTools.Method(typeof(TeleportWorld), "Teleport", new Type[1] { typeof(Player) }, (Type[])null), new HarmonyMethod(typeof(ThresholdTravel), "BeforeTeleport", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); HarmonyMethod val = new HarmonyMethod(typeof(ThresholdTravel), "LooksOpen", (Type[])null); harmony.Patch((MethodBase)AccessTools.Method(typeof(TeleportWorld), "HaveTarget", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(TeleportWorld), "TargetFound", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ThresholdArrival.Patch(harmony); ThresholdCarts.Patch(harmony); } private static bool BeforeTeleport(TeleportWorld __instance, Player player) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Invalid comparison between Unknown and I4 //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Invalid comparison between Unknown and I4 //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) try { if (vanillaJump || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsTeleporting() || !ThresholdModules.PortalRoutingActive()) { return true; } PortalEntry val = EntryOf(__instance); if (val == null) { return true; } if (!PortalModeRules.CanEnter(ThresholdModules.Mode, val)) { ((Character)player).Message((MessageType)2, ThresholdText.Text("picker_unnamed"), 0, (Sprite)null, false); return false; } Route val2 = RouteTable.Decide(Facts((PortalAction)2, val, null)); if ((int)((Route)(ref val2)).Kind != 1) { return true; } if ((Object)(object)Source == (Object)(object)__instance && InSession) { return false; } if (Host == null || (CanHost != null && !CanHost())) { return (int)ThresholdModules.Mode != 1; } sessionMode = ThresholdModules.Mode; Source = __instance; ThresholdPortals.RefreshNow(); walkIn.Request(__instance); return false; } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Threshold: walk-in failed, the game's portal runs: " + ex)); } Source = null; return true; } } public static void OpenPending() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Invalid comparison between Unknown and I4 if ((Object)(object)Source != (Object)null && sessionMode != ThresholdModules.Mode) { walkIn.Clear(); Source = null; CloseHost?.Invoke(); return; } TeleportWorld val = walkIn.Take(); if ((Object)(object)val == (Object)null || (Object)(object)Source != (Object)(object)val) { return; } Player localPlayer = Player.m_localPlayer; bool flag = false; try { flag = (Object)(object)localPlayer != (Object)null && Host != null && Host(val); } catch (Exception ex) { MeadworksLog? log = Plugin.Log; if (log != null) { log.Error((object)("Threshold: walk-in failed, the game's portal runs: " + ex)); } } if (flag) { return; } Source = null; if ((Object)(object)localPlayer == (Object)null || (Object)(object)val == (Object)null || (int)ThresholdModules.Mode == 1) { return; } vanillaJump = true; try { val.Teleport(localPlayer); } finally { vanillaJump = false; } } private static void LooksOpen(TeleportWorld __instance, ref bool __result) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 if (!((Object)(object)__instance == (Object)null) && ThresholdModules.PortalRoutingActive() && SourceZdo(__instance) != null) { int num; if ((int)ThresholdModules.Mode == 1) { PortalEntry? obj = EntryOf(__instance); num = ((obj != null && obj.IsTagged) ? 1 : 0); } else { num = 1; } __result = (byte)num != 0; } } public static void EndSession() { Source = null; } public static PortalEntry? ReturnHub() { //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) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 PortalEntry sourceEntry = SourceEntry; if (sourceEntry == null || !ThresholdModules.PortalRoutingActive()) { return null; } Route val = RouteTable.Decide(Facts((PortalAction)4, sourceEntry, null)); if ((int)((Route)(ref val)).Kind != 3) { return null; } return ((Route)(ref val)).Target; } public static bool TravelTo(PortalEntry destination) { //IL_001e: 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: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) PortalEntry sourceEntry = SourceEntry; if (sourceEntry == null || destination == null) { return false; } destination = TravelRules.CurrentTarget(destination, (IEnumerable<PortalEntry>)ThresholdPortals.Current); if (destination == null || !PortalModeRules.CanEnter(ThresholdModules.Mode, sourceEntry) || ((int)ThresholdModules.Mode == 1 && !destination.IsTagged)) { return false; } return Go(RouteTable.Decide(Facts((PortalAction)3, sourceEntry, destination))); } public static bool ReturnToHub() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) PortalEntry sourceEntry = SourceEntry; if (sourceEntry == null) { return false; } return Go(RouteTable.Decide(Facts((PortalAction)4, sourceEntry, null))); } private static bool Go(Route route) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Invalid comparison between Unknown and I4 //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Invalid comparison between Unknown and I4 //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Invalid comparison between Unknown and I4 //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Invalid comparison between Unknown and I4 //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Invalid comparison between Unknown and I4 //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; TeleportWorld source = Source; ThresholdConfig settings = Plugin.Settings; if ((Object)(object)localPlayer == (Object)null || (Object)(object)source == (Object)null || settings == null || !ThresholdModules.PortalRoutingActive()) { return false; } PortalEntry val = EntryOf(source); if (val == null || !PortalModeRules.CanEnter(ThresholdModules.Mode, val)) { return false; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); ZoneSystem instance = ZoneSystem.instance; bool flag = (Object)(object)instance != (Object)null && instance.GetGlobalKey((GlobalKeys)32); float num = 0f; bool flag2 = (Object)(object)instance != (Object)null && TravelRules.BossBlocked(instance.GetGlobalKey((GlobalKeys)33), (Object)(object)RandEventSystem.instance != (Object)null && RandEventSystem.instance.GetBossEvent() != null, instance.GetGlobalKey((GlobalKeys)47, ref num) ? num : 0f); bool ignoreTeleportRestrictions = settings.IgnoreTeleportRestrictions; bool flag3 = settings.Get<bool>(Fuel.FuelEnabled); string text = settings.Get<string>(Fuel.FuelItem); string text2 = FuelName(text); int num2 = ((flag3 && text2.Length > 0) ? inventory.CountItems(text2, -1, true) : 0); TravelPlan plan = TravelRules.Decide(new TravelFacts(route, flag, flag2, !inventory.IsTeleportable(true), Teleportable(inventory, settings), source.m_allowAllItems, ignoreTeleportRestrictions, flag3, settings.Get<int>(Fuel.FuelAmount), num2, settings.Get<OreTransportMode>(General.OreTransport))); if (!((TravelPlan)(ref plan)).Go) { Refuse(localPlayer, plan, (text2.Length > 0) ? text2 : text); return false; } PortalEntry target = ((TravelPlan)(ref pla