Decompiled source of ArchipelagoRandomizer v0.1.1
Archipelago.MultiClient.Net.dll
Decompiled 15 minutes 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.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.WebSockets; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using Archipelago.MultiClient.Net.ConcurrentCollection; using Archipelago.MultiClient.Net.Converters; using Archipelago.MultiClient.Net.DataPackage; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Exceptions; using Archipelago.MultiClient.Net.Extensions; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.MessageLog.Parts; using Archipelago.MultiClient.Net.Models; using Archipelago.MultiClient.Net.Packets; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: ComVisible(false)] [assembly: Guid("35a803ad-85ed-42e9-b1e3-c6b72096f0c1")] [assembly: InternalsVisibleTo("Archipelago.MultiClient.Net.Tests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Jarno Westhof, Hussein Farran, Zach Parks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyDescription("A client library for use with .NET based prog-langs for interfacing with Archipelago hosts.")] [assembly: AssemblyFileVersion("6.3.1.0")] [assembly: AssemblyInformationalVersion("6.3.1+3113ea01d1b0db22ada93b5f35c6d4272a8002b2")] [assembly: AssemblyProduct("Archipelago.MultiClient.Net")] [assembly: AssemblyTitle("Archipelago.MultiClient.Net")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ArchipelagoMW/Archipelago.MultiClient.Net")] [assembly: AssemblyVersion("6.3.1.0")] internal interface IConcurrentHashSet<T> { bool TryAdd(T item); bool Contains(T item); void UnionWith(T[] otherSet); T[] ToArray(); ReadOnlyCollection<T> AsToReadOnlyCollection(); ReadOnlyCollection<T> AsToReadOnlyCollectionExcept(IConcurrentHashSet<T> otherSet); } namespace Archipelago.MultiClient.Net { [Serializable] public abstract class ArchipelagoPacketBase { [JsonIgnore] internal JObject jobject; [JsonProperty("cmd")] [JsonConverter(typeof(StringEnumConverter))] public abstract ArchipelagoPacketType PacketType { get; } public JObject ToJObject() { return jobject; } } public interface IArchipelagoSession : IArchipelagoSessionActions { IArchipelagoSocketHelper Socket { get; } IReceivedItemsHelper Items { get; } ILocationCheckHelper Locations { get; } IPlayerHelper Players { get; } IDataStorageHelper DataStorage { get; } IConnectionInfoProvider ConnectionInfo { get; } IRoomStateHelper RoomState { get; } IMessageLogHelper MessageLog { get; } Task<RoomInfoPacket> ConnectAsync(); Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true); } public class ArchipelagoSession : IArchipelagoSession, IArchipelagoSessionActions { private const int ArchipelagoConnectionTimeoutInSeconds = 4; private ConnectionInfoHelper connectionInfo; private TaskCompletionSource<LoginResult> loginResultTask = new TaskCompletionSource<LoginResult>(); private TaskCompletionSource<RoomInfoPacket> roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); public IArchipelagoSocketHelper Socket { get; } public IReceivedItemsHelper Items { get; } public ILocationCheckHelper Locations { get; } public IPlayerHelper Players { get; } public IDataStorageHelper DataStorage { get; } public IConnectionInfoProvider ConnectionInfo => connectionInfo; public IRoomStateHelper RoomState { get; } public IMessageLogHelper MessageLog { get; } internal ArchipelagoSession(IArchipelagoSocketHelper socket, IReceivedItemsHelper items, ILocationCheckHelper locations, IPlayerHelper players, IRoomStateHelper roomState, ConnectionInfoHelper connectionInfoHelper, IDataStorageHelper dataStorage, IMessageLogHelper messageLog) { Socket = socket; Items = items; Locations = locations; Players = players; RoomState = roomState; connectionInfo = connectionInfoHelper; DataStorage = dataStorage; MessageLog = messageLog; socket.PacketReceived += Socket_PacketReceived; } private void Socket_PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket) && !(packet is ConnectionRefusedPacket)) { if (packet is RoomInfoPacket result) { roomInfoPacketTask.TrySetResult(result); } return; } if (packet is ConnectedPacket && RoomState.Version != null && RoomState.Version >= new Version(0, 3, 8)) { LogUsedVersion(); } loginResultTask.TrySetResult(LoginResult.FromPacket(packet)); } private void LogUsedVersion() { try { string fileVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; Socket.SendPacketAsync(new SetPacket { Key = ".NetUsedVersions", DefaultValue = (JToken)(object)JObject.FromObject((object)new Dictionary<string, bool>()), Operations = new OperationSpecification[1] { Operation.Update(new Dictionary<string, bool> { { ConnectionInfo.Game + ":" + fileVersion + ":NETSTANDARD2_0", true } }) } }); } catch { } } public Task<RoomInfoPacket> ConnectAsync() { roomInfoPacketTask = new TaskCompletionSource<RoomInfoPacket>(); Task.Factory.StartNew(delegate { try { Task task = Socket.ConnectAsync(); task.Wait(TimeSpan.FromSeconds(4.0)); if (!task.IsCompleted) { roomInfoPacketTask.TrySetCanceled(); } } catch (AggregateException) { roomInfoPacketTask.TrySetCanceled(); } }); return roomInfoPacketTask.Task; } public Task<LoginResult> LoginAsync(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { loginResultTask = new TaskCompletionSource<LoginResult>(); if (!roomInfoPacketTask.Task.IsCompleted) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } connectionInfo.SetConnectionParameters(game, tags, itemsHandlingFlags, uuid); try { Socket.SendPacket(BuildConnectPacket(name, password, version, requestSlotData)); } catch (ArchipelagoSocketClosedException) { loginResultTask.TrySetResult(new LoginFailure("You are not connected, run ConnectAsync() first")); return loginResultTask.Task; } SetResultAfterTimeout(loginResultTask, 4, new LoginFailure("Connection timed out.")); return loginResultTask.Task; } private static void SetResultAfterTimeout<T>(TaskCompletionSource<T> task, int timeoutInSeconds, T result) { new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)).Token.Register(delegate { task.TrySetResult(result); }); } public LoginResult TryConnectAndLogin(string game, string name, ItemsHandlingFlags itemsHandlingFlags, Version version = null, string[] tags = null, string uuid = null, string password = null, bool requestSlotData = true) { Task<RoomInfoPacket> task = ConnectAsync(); try { task.Wait(TimeSpan.FromSeconds(4.0)); } catch (AggregateException ex) { if (ex.GetBaseException() is OperationCanceledException) { return new LoginFailure("Connection timed out."); } return new LoginFailure(ex.GetBaseException().Message); } if (!task.IsCompleted) { return new LoginFailure("Connection timed out."); } return LoginAsync(game, name, itemsHandlingFlags, version, tags, uuid, password, requestSlotData).Result; } private ConnectPacket BuildConnectPacket(string name, string password, Version version, bool requestSlotData) { return new ConnectPacket { Game = ConnectionInfo.Game, Name = name, Password = password, Tags = ConnectionInfo.Tags, Uuid = ConnectionInfo.Uuid, Version = ((version != null) ? new NetworkVersion(version) : new NetworkVersion(0, 4, 0)), ItemsHandling = ConnectionInfo.ItemsHandlingFlags, RequestSlotData = requestSlotData }; } public void Say(string message) { Socket.SendPacket(new SayPacket { Text = message }); } public void SetClientState(ArchipelagoClientState state) { Socket.SendPacket(new StatusUpdatePacket { Status = state }); } public void SetGoalAchieved() { SetClientState(ArchipelagoClientState.ClientGoal); } } public interface IArchipelagoSessionActions { void Say(string message); void SetClientState(ArchipelagoClientState state); void SetGoalAchieved(); } public static class ArchipelagoSessionFactory { public static ArchipelagoSession CreateSession(Uri uri) { ArchipelagoSocketHelper socket = new ArchipelagoSocketHelper(uri); DataPackageCache cache = new DataPackageCache(socket); ConnectionInfoHelper connectionInfoHelper = new ConnectionInfoHelper(socket); PlayerHelper playerHelper = new PlayerHelper(socket, connectionInfoHelper); ItemInfoResolver itemInfoResolver = new ItemInfoResolver(cache, connectionInfoHelper); LocationCheckHelper locationCheckHelper = new LocationCheckHelper(socket, itemInfoResolver, connectionInfoHelper, playerHelper); ReceivedItemsHelper items = new ReceivedItemsHelper(socket, locationCheckHelper, itemInfoResolver, connectionInfoHelper, playerHelper); RoomStateHelper roomState = new RoomStateHelper(socket, locationCheckHelper); DataStorageHelper dataStorage = new DataStorageHelper(socket, connectionInfoHelper); MessageLogHelper messageLog = new MessageLogHelper(socket, itemInfoResolver, playerHelper, connectionInfoHelper); return new ArchipelagoSession(socket, items, locationCheckHelper, playerHelper, roomState, connectionInfoHelper, dataStorage, messageLog); } public static ArchipelagoSession CreateSession(string hostname, int port = 38281) { return CreateSession(ParseUri(hostname, port)); } internal static Uri ParseUri(string hostname, int port) { string text = hostname; if (!text.StartsWith("ws://") && !text.StartsWith("wss://")) { text = "unspecified://" + text; } if (!text.Substring(text.IndexOf("://", StringComparison.Ordinal) + 3).Contains(":")) { text += $":{port}"; } if (text.EndsWith(":")) { text += port; } return new Uri(text); } } public abstract class LoginResult { public abstract bool Successful { get; } public static LoginResult FromPacket(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket connectionRefusedPacket) { return new LoginFailure(connectionRefusedPacket); } throw new ArgumentOutOfRangeException("packet", "packet is not a connection result packet"); } return new LoginSuccessful(connectedPacket); } } public class LoginSuccessful : LoginResult { public override bool Successful => true; public int Team { get; } public int Slot { get; } public Dictionary<string, object> SlotData { get; } public LoginSuccessful(ConnectedPacket connectedPacket) { Team = connectedPacket.Team; Slot = connectedPacket.Slot; SlotData = connectedPacket.SlotData; } } public class LoginFailure : LoginResult { public override bool Successful => false; public ConnectionRefusedError[] ErrorCodes { get; } public string[] Errors { get; } public LoginFailure(ConnectionRefusedPacket connectionRefusedPacket) { if (connectionRefusedPacket.Errors != null) { ErrorCodes = connectionRefusedPacket.Errors.ToArray(); Errors = ErrorCodes.Select(GetErrorMessage).ToArray(); } else { ErrorCodes = new ConnectionRefusedError[0]; Errors = new string[0]; } } public LoginFailure(string message) { ErrorCodes = new ConnectionRefusedError[0]; Errors = new string[1] { message }; } private static string GetErrorMessage(ConnectionRefusedError errorCode) { return errorCode switch { ConnectionRefusedError.InvalidSlot => "The slot name did not match any slot on the server.", ConnectionRefusedError.InvalidGame => "The slot is set to a different game on the server.", ConnectionRefusedError.SlotAlreadyTaken => "The slot already has a connection with a different uuid established.", ConnectionRefusedError.IncompatibleVersion => "The client and server version mismatch.", ConnectionRefusedError.InvalidPassword => "The password is invalid.", ConnectionRefusedError.InvalidItemsHandling => "The item handling flags provided are invalid.", _ => $"Unknown error: {errorCode}.", }; } } internal class TwoWayLookup<TA, TB> : IEnumerable<KeyValuePair<TB, TA>>, IEnumerable { private readonly Dictionary<TA, TB> aToB = new Dictionary<TA, TB>(); private readonly Dictionary<TB, TA> bToA = new Dictionary<TB, TA>(); public TA this[TB b] => bToA[b]; public TB this[TA a] => aToB[a]; public void Add(TA a, TB b) { aToB[a] = b; bToA[b] = a; } public void Add(TB b, TA a) { Add(a, b); } public bool TryGetValue(TA a, out TB b) { return aToB.TryGetValue(a, out b); } public bool TryGetValue(TB b, out TA a) { return bToA.TryGetValue(b, out a); } public IEnumerator<KeyValuePair<TB, TA>> GetEnumerator() { return bToA.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } } namespace Archipelago.MultiClient.Net.Packets { public class BouncedPacket : BouncePacket { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounced; } public class BouncePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Bounce; [JsonProperty("games")] public List<string> Games { get; set; } = new List<string>(); [JsonProperty("slots")] public List<int> Slots { get; set; } = new List<int>(); [JsonProperty("tags")] public List<string> Tags { get; set; } = new List<string>(); [JsonProperty("data")] public Dictionary<string, JToken> Data { get; set; } } public class ConnectedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connected; [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("missing_locations")] public long[] MissingChecks { get; set; } [JsonProperty("checked_locations")] public long[] LocationsChecked { get; set; } [JsonProperty("slot_data")] public Dictionary<string, object> SlotData { get; set; } [JsonProperty("slot_info")] public Dictionary<int, NetworkSlot> SlotInfo { get; set; } [JsonProperty("hint_points")] public int? HintPoints { get; set; } } public class ConnectionRefusedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectionRefused; [JsonProperty("errors", ItemConverterType = typeof(StringEnumConverter))] public ConnectionRefusedError[] Errors { get; set; } } public class ConnectPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Connect; [JsonProperty("password")] public string Password { get; set; } [JsonProperty("game")] public string Game { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("uuid")] public string Uuid { get; set; } [JsonProperty("version")] public NetworkVersion Version { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("items_handling")] public ItemsHandlingFlags ItemsHandling { get; set; } [JsonProperty("slot_data")] public bool RequestSlotData { get; set; } } public class ConnectUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ConnectUpdate; [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("items_handling")] public ItemsHandlingFlags? ItemsHandling { get; set; } } public class DataPackagePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.DataPackage; [JsonProperty("data")] public Archipelago.MultiClient.Net.Models.DataPackage DataPackage { get; set; } } public class GetDataPackagePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.GetDataPackage; [JsonProperty("games")] public string[] Games { get; set; } } public class GetPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Get; [JsonProperty("keys")] public string[] Keys { get; set; } } public class InvalidPacketPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.InvalidPacket; [JsonProperty("type")] public InvalidPacketErrorType ErrorType { get; set; } [JsonProperty("text")] public string ErrorText { get; set; } [JsonProperty("original_cmd")] public ArchipelagoPacketType OriginalCmd { get; set; } } public class LocationChecksPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationChecks; [JsonProperty("locations")] public long[] Locations { get; set; } } public class LocationInfoPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationInfo; [JsonProperty("locations")] public NetworkItem[] Locations { get; set; } } public class LocationScoutsPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.LocationScouts; [JsonProperty("locations")] public long[] Locations { get; set; } [JsonProperty("create_as_hint")] public int CreateAsHint { get; set; } } public class PrintJsonPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.PrintJSON; [JsonProperty("data")] public JsonMessagePart[] Data { get; set; } [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] public JsonMessageType? MessageType { get; set; } } public class ItemPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } } public class ItemCheatPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } [JsonProperty("team")] public int Team { get; set; } } public class HintPrintJsonPacket : PrintJsonPacket { [JsonProperty("receiving")] public int ReceivingPlayer { get; set; } [JsonProperty("item")] public NetworkItem Item { get; set; } [JsonProperty("found")] public bool? Found { get; set; } } public class JoinPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } } public class LeavePrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class ChatPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("message")] public string Message { get; set; } } public class ServerChatPrintJsonPacket : PrintJsonPacket { [JsonProperty("message")] public string Message { get; set; } } public class TutorialPrintJsonPacket : PrintJsonPacket { } public class TagsChangedPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } } public class CommandResultPrintJsonPacket : PrintJsonPacket { } public class AdminCommandResultPrintJsonPacket : PrintJsonPacket { } public class GoalPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class ReleasePrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class CollectPrintJsonPacket : PrintJsonPacket { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } } public class CountdownPrintJsonPacket : PrintJsonPacket { [JsonProperty("countdown")] public int RemainingSeconds { get; set; } } public class ReceivedItemsPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.ReceivedItems; [JsonProperty("index")] public int Index { get; set; } [JsonProperty("items")] public NetworkItem[] Items { get; set; } } public class RetrievedPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Retrieved; [JsonProperty("keys")] public Dictionary<string, JToken> Data { get; set; } } public class RoomInfoPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomInfo; [JsonProperty("version")] public NetworkVersion Version { get; set; } [JsonProperty("generator_version")] public NetworkVersion GeneratorVersion { get; set; } [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("password")] public bool Password { get; set; } [JsonProperty("permissions")] public Dictionary<string, Permissions> Permissions { get; set; } [JsonProperty("hint_cost")] public int HintCostPercentage { get; set; } [JsonProperty("location_check_points")] public int LocationCheckPoints { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("games")] public string[] Games { get; set; } [JsonProperty("datapackage_checksums")] public Dictionary<string, string> DataPackageChecksums { get; set; } [JsonProperty("seed_name")] public string SeedName { get; set; } [JsonProperty("time")] public double Timestamp { get; set; } } public class RoomUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.RoomUpdate; [JsonProperty("tags")] public string[] Tags { get; set; } [JsonProperty("password")] public bool? Password { get; set; } [JsonProperty("permissions")] public Dictionary<string, Permissions> Permissions { get; set; } = new Dictionary<string, Permissions>(); [JsonProperty("hint_cost")] public int? HintCostPercentage { get; set; } [JsonProperty("location_check_points")] public int? LocationCheckPoints { get; set; } [JsonProperty("players")] public NetworkPlayer[] Players { get; set; } [JsonProperty("hint_points")] public int? HintPoints { get; set; } [JsonProperty("checked_locations")] public long[] CheckedLocations { get; set; } } public class SayPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Say; [JsonProperty("text")] public string Text { get; set; } } public class SetNotifyPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetNotify; [JsonProperty("keys")] public string[] Keys { get; set; } } public class SetPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Set; [JsonProperty("key")] public string Key { get; set; } [JsonProperty("default")] public JToken DefaultValue { get; set; } [JsonProperty("operations")] public OperationSpecification[] Operations { get; set; } [JsonProperty("want_reply")] public bool WantReply { get; set; } [JsonExtensionData] public Dictionary<string, JToken> AdditionalArguments { get; set; } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext context) { AdditionalArguments?.Remove("cmd"); } } public class SetReplyPacket : SetPacket { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.SetReply; [JsonProperty("value")] public JToken Value { get; set; } [JsonProperty("original_value")] public JToken OriginalValue { get; set; } } public class StatusUpdatePacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.StatusUpdate; [JsonProperty("status")] public ArchipelagoClientState Status { get; set; } } public class SyncPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Sync; } internal class UnknownPacket : ArchipelagoPacketBase { public override ArchipelagoPacketType PacketType => ArchipelagoPacketType.Unknown; } } namespace Archipelago.MultiClient.Net.Models { public struct Color : IEquatable<Color> { public static Color Red = new Color(byte.MaxValue, 0, 0); public static Color Green = new Color(0, 128, 0); public static Color Yellow = new Color(byte.MaxValue, byte.MaxValue, 0); public static Color Blue = new Color(0, 0, byte.MaxValue); public static Color Magenta = new Color(byte.MaxValue, 0, byte.MaxValue); public static Color Cyan = new Color(0, byte.MaxValue, byte.MaxValue); public static Color Black = new Color(0, 0, 0); public static Color White = new Color(byte.MaxValue, byte.MaxValue, byte.MaxValue); public static Color SlateBlue = new Color(106, 90, 205); public static Color Salmon = new Color(250, 128, 114); public static Color Plum = new Color(221, 160, 221); public byte R { get; set; } public byte G { get; set; } public byte B { get; set; } public Color(byte r, byte g, byte b) { R = r; G = g; B = b; } public override bool Equals(object obj) { if (obj is Color color && R == color.R && G == color.G) { return B == color.B; } return false; } public bool Equals(Color other) { if (R == other.R && G == other.G) { return B == other.B; } return false; } public override int GetHashCode() { return ((-1520100960 * -1521134295 + R.GetHashCode()) * -1521134295 + G.GetHashCode()) * -1521134295 + B.GetHashCode(); } public static bool operator ==(Color left, Color right) { return left.Equals(right); } public static bool operator !=(Color left, Color right) { return !(left == right); } } public class DataPackage { [JsonProperty("games")] public Dictionary<string, GameData> Games { get; set; } = new Dictionary<string, GameData>(); } public class DataStorageElement { internal DataStorageElementContext Context; internal List<OperationSpecification> Operations = new List<OperationSpecification>(0); internal DataStorageHelper.DataStorageUpdatedHandler Callbacks; internal Dictionary<string, JToken> AdditionalArguments = new Dictionary<string, JToken>(0); private JToken cachedValue; public event DataStorageHelper.DataStorageUpdatedHandler OnValueChanged { add { Context.AddHandler(Context.Key, value); } remove { Context.RemoveHandler(Context.Key, value); } } internal DataStorageElement(DataStorageElementContext context) { Context = context; } internal DataStorageElement(OperationType operationType, JToken value) { Operations = new List<OperationSpecification>(1) { new OperationSpecification { OperationType = operationType, Value = value } }; } internal DataStorageElement(DataStorageElement source, OperationType operationType, JToken value) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Operations.Add(new OperationSpecification { OperationType = operationType, Value = value }); } internal DataStorageElement(DataStorageElement source, Callback callback) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; Callbacks = (DataStorageHelper.DataStorageUpdatedHandler)Delegate.Combine(Callbacks, callback.Method); } internal DataStorageElement(DataStorageElement source, AdditionalArgument additionalArgument) : this(source.Context) { Operations = source.Operations.ToList(); Callbacks = source.Callbacks; AdditionalArguments = source.AdditionalArguments; AdditionalArguments[additionalArgument.Key] = additionalArgument.Value; } public static DataStorageElement operator ++(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(1)); } public static DataStorageElement operator --(DataStorageElement a) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(-1)); } public static DataStorageElement operator +(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, string b) { return new DataStorageElement(a, OperationType.Add, JToken.op_Implicit(b)); } public static DataStorageElement operator +(DataStorageElement a, JToken b) { return new DataStorageElement(a, OperationType.Add, b); } public static DataStorageElement operator +(DataStorageElement a, IEnumerable b) { return new DataStorageElement(a, OperationType.Add, (JToken)(object)JArray.FromObject((object)b)); } public static DataStorageElement operator +(DataStorageElement a, OperationSpecification s) { return new DataStorageElement(a, s.OperationType, s.Value); } public static DataStorageElement operator +(DataStorageElement a, Callback c) { return new DataStorageElement(a, c); } public static DataStorageElement operator +(DataStorageElement a, AdditionalArgument arg) { return new DataStorageElement(a, arg); } public static DataStorageElement operator *(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator *(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator %(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mod, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator ^(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Pow, JToken.op_Implicit(b)); } public static DataStorageElement operator -(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator -(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0f - b))); } public static DataStorageElement operator -(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(0.0 - b))); } public static DataStorageElement operator -(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Add, JToken.FromObject((object)(-b))); } public static DataStorageElement operator /(DataStorageElement a, int b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, long b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / (decimal)b))); } public static DataStorageElement operator /(DataStorageElement a, float b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / (double)b))); } public static DataStorageElement operator /(DataStorageElement a, double b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1.0 / b))); } public static DataStorageElement operator /(DataStorageElement a, decimal b) { return new DataStorageElement(a, OperationType.Mul, JToken.FromObject((object)(1m / b))); } public static implicit operator DataStorageElement(bool b) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(b)); } public static implicit operator DataStorageElement(int i) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(i)); } public static implicit operator DataStorageElement(long l) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(l)); } public static implicit operator DataStorageElement(decimal m) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(m)); } public static implicit operator DataStorageElement(double d) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(d)); } public static implicit operator DataStorageElement(float f) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(f)); } public static implicit operator DataStorageElement(string s) { if (s != null) { return new DataStorageElement(OperationType.Replace, JToken.op_Implicit(s)); } return new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } public static implicit operator DataStorageElement(JToken o) { return new DataStorageElement(OperationType.Replace, o); } public static implicit operator DataStorageElement(Array a) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)a)); } public static implicit operator DataStorageElement(List<bool> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<int> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<long> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<decimal> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<double> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<float> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<string> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator DataStorageElement(List<object> l) { return new DataStorageElement(OperationType.Replace, (JToken)(object)JArray.FromObject((object)l)); } public static implicit operator bool(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool>(e); } public static implicit operator bool?(DataStorageElement e) { return RetrieveAndReturnBoolValue<bool?>(e); } public static implicit operator int(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int>(e); } public static implicit operator int?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<int?>(e); } public static implicit operator long(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long>(e); } public static implicit operator long?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<long?>(e); } public static implicit operator float(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float>(e); } public static implicit operator float?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<float?>(e); } public static implicit operator double(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double>(e); } public static implicit operator double?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<double?>(e); } public static implicit operator decimal(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal>(e); } public static implicit operator decimal?(DataStorageElement e) { return RetrieveAndReturnDecimalValue<decimal?>(e); } public static implicit operator string(DataStorageElement e) { return RetrieveAndReturnStringValue(e); } public static implicit operator bool[](DataStorageElement e) { return RetrieveAndReturnArrayValue<bool[]>(e); } public static implicit operator int[](DataStorageElement e) { return RetrieveAndReturnArrayValue<int[]>(e); } public static implicit operator long[](DataStorageElement e) { return RetrieveAndReturnArrayValue<long[]>(e); } public static implicit operator decimal[](DataStorageElement e) { return RetrieveAndReturnArrayValue<decimal[]>(e); } public static implicit operator double[](DataStorageElement e) { return RetrieveAndReturnArrayValue<double[]>(e); } public static implicit operator float[](DataStorageElement e) { return RetrieveAndReturnArrayValue<float[]>(e); } public static implicit operator string[](DataStorageElement e) { return RetrieveAndReturnArrayValue<string[]>(e); } public static implicit operator object[](DataStorageElement e) { return RetrieveAndReturnArrayValue<object[]>(e); } public static implicit operator List<bool>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<bool>>(e); } public static implicit operator List<int>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<int>>(e); } public static implicit operator List<long>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<long>>(e); } public static implicit operator List<decimal>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<decimal>>(e); } public static implicit operator List<double>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<double>>(e); } public static implicit operator List<float>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<float>>(e); } public static implicit operator List<string>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<string>>(e); } public static implicit operator List<object>(DataStorageElement e) { return RetrieveAndReturnArrayValue<List<object>>(e); } public static implicit operator Array(DataStorageElement e) { return RetrieveAndReturnArrayValue<Array>(e); } public static implicit operator JArray(DataStorageElement e) { return RetrieveAndReturnArrayValue<JArray>(e); } public static implicit operator JToken(DataStorageElement e) { return e.Context.GetData(e.Context.Key); } public static DataStorageElement operator +(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse(b.ToString())); } public static DataStorageElement operator *(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mul, JToken.Parse(b.ToString())); } public static DataStorageElement operator %(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Mod, JToken.Parse(b.ToString())); } public static DataStorageElement operator ^(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Pow, JToken.Parse(b.ToString())); } public static DataStorageElement operator -(DataStorageElement a, BigInteger b) { return new DataStorageElement(a, OperationType.Add, JToken.Parse((-b).ToString())); } public static DataStorageElement operator /(DataStorageElement a, BigInteger b) { throw new InvalidOperationException("DataStorage[Key] / BigInterger is not supported, due to loss of precision when using integer division"); } public static implicit operator DataStorageElement(BigInteger bi) { return new DataStorageElement(OperationType.Replace, JToken.Parse(bi.ToString())); } public static implicit operator BigInteger(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger>(e); } public static implicit operator BigInteger?(DataStorageElement e) { return RetrieveAndReturnBigIntegerValue<BigInteger?>(e); } private static T RetrieveAndReturnBigIntegerValue<T>(DataStorageElement e) { if (e.cachedValue != null) { if (!BigInteger.TryParse(((object)e.cachedValue).ToString(), out var result)) { return default(T); } return (T)Convert.ChangeType(result, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } BigInteger result2; BigInteger? bigInteger = (BigInteger.TryParse(((object)e.Context.GetData(e.Context.Key)).ToString(), out result2) ? new BigInteger?(result2) : null); if (!bigInteger.HasValue && !IsNullable<T>()) { bigInteger = Activator.CreateInstance<BigInteger>(); } foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Floor || operation.OperationType == OperationType.Ceil) { continue; } if (!BigInteger.TryParse(((object)operation.Value).ToString(), NumberStyles.AllowLeadingSign, null, out var result3)) { throw new InvalidOperationException($"DataStorage[Key] cannot be converted to BigInterger as its value its not an integer number, value: {operation.Value}"); } switch (operation.OperationType) { case OperationType.Replace: bigInteger = result3; break; case OperationType.Add: bigInteger += result3; break; case OperationType.Mul: bigInteger *= result3; break; case OperationType.Mod: bigInteger %= result3; break; case OperationType.Pow: bigInteger = BigInteger.Pow(bigInteger.Value, (int)operation.Value); break; case OperationType.Max: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value > bigInteger2) { bigInteger = result3; } break; } case OperationType.Min: { BigInteger value = result3; BigInteger? bigInteger2 = bigInteger; if (value < bigInteger2) { bigInteger = result3; } break; } case OperationType.Xor: bigInteger ^= result3; break; case OperationType.Or: bigInteger |= result3; break; case OperationType.And: bigInteger &= result3; break; case OperationType.LeftShift: bigInteger <<= (int)operation.Value; break; case OperationType.RightShift: bigInteger >>= (int)operation.Value; break; } } e.cachedValue = JToken.Parse(bigInteger.ToString()); if (!bigInteger.HasValue) { return default(T); } return (T)Convert.ChangeType(bigInteger.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } public void Initialize(JToken value) { Context.Initialize(Context.Key, value); } public void Initialize(IEnumerable value) { Context.Initialize(Context.Key, (JToken)(object)JArray.FromObject((object)value)); } public Task<T> GetAsync<T>() { return GetAsync().ContinueWith((Task<JToken> r) => r.Result.ToObject<T>()); } public Task<JToken> GetAsync() { return Context.GetAsync(Context.Key); } private static T RetrieveAndReturnArrayValue<T>(DataStorageElement e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (e.cachedValue != null) { return ((JToken)(JArray)e.cachedValue).ToObject<T>(); } JArray val = (JArray)(((object)e.Context.GetData(e.Context.Key).ToObject<JArray>()) ?? ((object)new JArray())); foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Add: if ((int)operation.Value.Type != 2) { throw new InvalidOperationException($"Cannot perform operation {OperationType.Add} on Array value, with a non Array value: {operation.Value}"); } ((JContainer)val).Merge((object)operation.Value); break; case OperationType.Replace: if ((int)operation.Value.Type != 2) { throw new InvalidOperationException($"Cannot replace Array value, with a non Array value: {operation.Value}"); } val = (JArray)(((object)operation.Value.ToObject<JArray>()) ?? ((object)new JArray())); break; default: throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on Array value"); } } e.cachedValue = (JToken)(object)val; return ((JToken)val).ToObject<T>(); } private static string RetrieveAndReturnStringValue(DataStorageElement e) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Invalid comparison between Unknown and I4 if (e.cachedValue != null) { return (string)e.cachedValue; } JToken val = e.Context.GetData(e.Context.Key); string text = (((int)val.Type == 10) ? null : ((object)val).ToString()); foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Add: text += (string)operation.Value; break; case OperationType.Mul: if ((int)operation.Value.Type != 6) { throw new InvalidOperationException($"Cannot perform operation {OperationType.Mul} on string value, with a non interger value: {operation.Value}"); } text = string.Concat(Enumerable.Repeat(text, (int)operation.Value)); break; case OperationType.Replace: text = (string)operation.Value; break; default: throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on string value"); } } if (text == null) { e.cachedValue = (JToken)(object)JValue.CreateNull(); } else { e.cachedValue = JToken.op_Implicit(text); } return (string)e.cachedValue; } private static T RetrieveAndReturnBoolValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } bool? flag = e.Context.GetData(e.Context.Key).ToObject<bool?>() ?? ((bool?)Activator.CreateInstance(typeof(T))); foreach (OperationSpecification operation in e.Operations) { if (operation.OperationType == OperationType.Replace) { flag = (bool?)operation.Value; continue; } throw new InvalidOperationException($"Cannot perform operation {operation.OperationType} on boolean value"); } e.cachedValue = JToken.op_Implicit(flag); if (!flag.HasValue) { return default(T); } return (T)Convert.ChangeType(flag.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static T RetrieveAndReturnDecimalValue<T>(DataStorageElement e) { if (e.cachedValue != null) { return e.cachedValue.ToObject<T>(); } decimal? num = e.Context.GetData(e.Context.Key).ToObject<decimal?>(); if (!num.HasValue && !IsNullable<T>()) { num = Activator.CreateInstance<decimal>(); } foreach (OperationSpecification operation in e.Operations) { switch (operation.OperationType) { case OperationType.Replace: num = (decimal)operation.Value; break; case OperationType.Add: num += (decimal?)(decimal)operation.Value; break; case OperationType.Mul: num *= (decimal?)(decimal)operation.Value; break; case OperationType.Mod: num %= (decimal?)(decimal)operation.Value; break; case OperationType.Pow: num = (decimal)Math.Pow((double)num.Value, (double)operation.Value); break; case OperationType.Max: num = Math.Max(num.Value, (decimal)operation.Value); break; case OperationType.Min: num = Math.Min(num.Value, (decimal)operation.Value); break; case OperationType.Xor: num = (long)num.Value ^ (long)operation.Value; break; case OperationType.Or: num = (long)num.Value | (long)operation.Value; break; case OperationType.And: num = (long)num.Value & (long)operation.Value; break; case OperationType.LeftShift: num = (long)num.Value << (int)operation.Value; break; case OperationType.RightShift: num = (long)num.Value >> (int)operation.Value; break; case OperationType.Floor: num = Math.Floor(num.Value); break; case OperationType.Ceil: num = Math.Ceiling(num.Value); break; } } e.cachedValue = JToken.op_Implicit(num); if (!num.HasValue) { return default(T); } return (T)Convert.ChangeType(num.Value, IsNullable<T>() ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T)); } private static bool IsNullable<T>() { if (typeof(T).IsGenericType) { return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition(); } return false; } public T To<T>() { if (Operations.Count != 0) { throw new InvalidOperationException("DataStorageElement.To<T>() cannot be used together with other operations on the DataStorageElement"); } return Context.GetData(Context.Key).ToObject<T>(); } public override string ToString() { return (Context?.ToString() ?? "(null)") + ", (" + ListOperations() + ")"; } private string ListOperations() { if (Operations != null) { return string.Join(", ", Operations.Select((OperationSpecification o) => o.ToString()).ToArray()); } return "none"; } } internal class DataStorageElementContext { internal string Key { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> AddHandler { get; set; } internal Action<string, DataStorageHelper.DataStorageUpdatedHandler> RemoveHandler { get; set; } internal Func<string, JToken> GetData { get; set; } internal Action<string, JToken> Initialize { get; set; } internal Func<string, Task<JToken>> GetAsync { get; set; } public override string ToString() { return "Key: " + Key; } } public class GameData { [JsonProperty("location_name_to_id")] public Dictionary<string, long> LocationLookup { get; set; } = new Dictionary<string, long>(); [JsonProperty("item_name_to_id")] public Dictionary<string, long> ItemLookup { get; set; } = new Dictionary<string, long>(); [Obsolete("use Checksum instead")] [JsonProperty("version")] public int Version { get; set; } [JsonProperty("checksum")] public string Checksum { get; set; } } public class Hint { [JsonProperty("receiving_player")] public int ReceivingPlayer { get; set; } [JsonProperty("finding_player")] public int FindingPlayer { get; set; } [JsonProperty("item")] public long ItemId { get; set; } [JsonProperty("location")] public long LocationId { get; set; } [JsonProperty("item_flags")] public ItemFlags ItemFlags { get; set; } [JsonProperty("found")] public bool Found { get; set; } [JsonProperty("entrance")] public string Entrance { get; set; } } public class ItemInfo { private readonly IItemInfoResolver itemInfoResolver; public long ItemId { get; } public long LocationId { get; } public PlayerInfo Player { get; } public ItemFlags Flags { get; } public string ItemName => itemInfoResolver.GetItemName(ItemId, ItemGame); public string ItemDisplayName => ItemName ?? $"Item: {ItemId}"; public string LocationName => itemInfoResolver.GetLocationName(LocationId, LocationGame); public string LocationDisplayName => LocationName ?? $"Location: {LocationId}"; public string ItemGame { get; } public string LocationGame { get; } public ItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, PlayerInfo player) { this.itemInfoResolver = itemInfoResolver; ItemGame = receiverGame; LocationGame = senderGame; ItemId = item.Item; LocationId = item.Location; Flags = item.Flags; Player = player; } public SerializableItemInfo ToSerializable() { return new SerializableItemInfo { IsScout = (GetType() == typeof(ScoutedItemInfo)), ItemId = ItemId, LocationId = LocationId, PlayerSlot = Player, Player = Player, Flags = Flags, ItemGame = ItemGame, ItemName = ItemName, LocationGame = LocationGame, LocationName = LocationName }; } } public class ScoutedItemInfo : ItemInfo { public new PlayerInfo Player => base.Player; public bool IsReceiverRelatedToActivePlayer { get; } public ScoutedItemInfo(NetworkItem item, string receiverGame, string senderGame, IItemInfoResolver itemInfoResolver, IPlayerHelper players, PlayerInfo player) : base(item, receiverGame, senderGame, itemInfoResolver, player) { IsReceiverRelatedToActivePlayer = (players.ActivePlayer ?? new PlayerInfo()).IsRelatedTo(player); } } public class JsonMessagePart { [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public JsonMessagePartType? Type { get; set; } [JsonProperty("color")] [JsonConverter(typeof(StringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public JsonMessagePartColor? Color { get; set; } [JsonProperty("text")] public string Text { get; set; } [JsonProperty("player")] public int? Player { get; set; } [JsonProperty("flags")] public ItemFlags? Flags { get; set; } } public struct NetworkItem { [JsonProperty("item")] public long Item { get; set; } [JsonProperty("location")] public long Location { get; set; } [JsonProperty("player")] public int Player { get; set; } [JsonProperty("flags")] public ItemFlags Flags { get; set; } } public struct NetworkPlayer { [JsonProperty("team")] public int Team { get; set; } [JsonProperty("slot")] public int Slot { get; set; } [JsonProperty("alias")] public string Alias { get; set; } [JsonProperty("name")] public string Name { get; set; } } public struct NetworkSlot { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("game")] public string Game { get; set; } [JsonProperty("type")] public SlotType Type { get; set; } [JsonProperty("group_members")] public int[] GroupMembers { get; set; } } public class NetworkVersion { [JsonProperty("major")] public int Major { get; set; } [JsonProperty("minor")] public int Minor { get; set; } [JsonProperty("build")] public int Build { get; set; } [JsonProperty("class")] public string Class => "Version"; public NetworkVersion() { } public NetworkVersion(int major, int minor, int build) { Major = major; Minor = minor; Build = build; } public NetworkVersion(Version version) { Major = version.Major; Minor = version.Minor; Build = version.Build; } public Version ToVersion() { return new Version(Major, Minor, Build); } } public class OperationSpecification { [JsonProperty("operation")] [JsonConverter(typeof(StringEnumConverter), new object[] { typeof(SnakeCaseNamingStrategy) })] public OperationType OperationType; [JsonProperty("value")] public JToken Value { get; set; } public override string ToString() { return $"{OperationType}: {Value}"; } } public static class Operation { public static OperationSpecification Min(int i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(long i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(float i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(double i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(decimal i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Min(JToken i) { return new OperationSpecification { OperationType = OperationType.Min, Value = i }; } public static OperationSpecification Min(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Min, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Max(int i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(long i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(float i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(double i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(decimal i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Max(JToken i) { return new OperationSpecification { OperationType = OperationType.Max, Value = i }; } public static OperationSpecification Max(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Max, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Remove(JToken value) { return new OperationSpecification { OperationType = OperationType.Remove, Value = value }; } public static OperationSpecification Pop(int value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = JToken.op_Implicit(value) }; } public static OperationSpecification Pop(JToken value) { return new OperationSpecification { OperationType = OperationType.Pop, Value = value }; } public static OperationSpecification Update(IDictionary dictionary) { return new OperationSpecification { OperationType = OperationType.Update, Value = (JToken)(object)JObject.FromObject((object)dictionary) }; } public static OperationSpecification Floor() { return new OperationSpecification { OperationType = OperationType.Floor, Value = null }; } public static OperationSpecification Ceiling() { return new OperationSpecification { OperationType = OperationType.Ceil, Value = null }; } } public static class Bitwise { public static OperationSpecification Xor(long i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Xor(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Xor, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification Or(long i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.op_Implicit(i) }; } public static OperationSpecification Or(BigInteger i) { return new OperationSpecification { OperationType = OperationType.Or, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification And(long i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.op_Implicit(i) }; } public static OperationSpecification And(BigInteger i) { return new OperationSpecification { OperationType = OperationType.And, Value = JToken.Parse(i.ToString()) }; } public static OperationSpecification LeftShift(long i) { return new OperationSpecification { OperationType = OperationType.LeftShift, Value = JToken.op_Implicit(i) }; } public static OperationSpecification RightShift(long i) { return new OperationSpecification { OperationType = OperationType.RightShift, Value = JToken.op_Implicit(i) }; } } public class Callback { internal DataStorageHelper.DataStorageUpdatedHandler Method { get; set; } private Callback() { } public static Callback Add(DataStorageHelper.DataStorageUpdatedHandler callback) { return new Callback { Method = callback }; } } public class AdditionalArgument { internal string Key { get; set; } internal JToken Value { get; set; } private AdditionalArgument() { } public static AdditionalArgument Add(string name, JToken value) { return new AdditionalArgument { Key = name, Value = value }; } } public class MinimalSerializableItemInfo { public long ItemId { get; set; } public long LocationId { get; set; } public int PlayerSlot { get; set; } public ItemFlags Flags { get; set; } public string ItemGame { get; set; } public string LocationGame { get; set; } } public class SerializableItemInfo : MinimalSerializableItemInfo { public bool IsScout { get; set; } public PlayerInfo Player { get; set; } public string ItemName { get; set; } public string LocationName { get; set; } [JsonIgnore] public string ItemDisplayName => ItemName ?? $"Item: {base.ItemId}"; [JsonIgnore] public string LocationDisplayName => LocationName ?? $"Location: {base.LocationId}"; public string ToJson(bool full = false) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown MinimalSerializableItemInfo minimalSerializableItemInfo = this; if (!full) { minimalSerializableItemInfo = new MinimalSerializableItemInfo { ItemId = base.ItemId, LocationId = base.LocationId, PlayerSlot = base.PlayerSlot, Flags = base.Flags }; if (IsScout) { minimalSerializableItemInfo.ItemGame = base.ItemGame; } else { minimalSerializableItemInfo.LocationGame = base.LocationGame; } } JsonSerializerSettings val = new JsonSerializerSettings { NullValueHandling = (NullValueHandling)1, Formatting = (Formatting)0 }; return JsonConvert.SerializeObject((object)minimalSerializableItemInfo, val); } public static SerializableItemInfo FromJson(string json, IArchipelagoSession session = null) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown ItemInfoStreamingContext additional = ((session != null) ? new ItemInfoStreamingContext { Items = session.Items, Locations = session.Locations, PlayerHelper = session.Players, ConnectionInfo = session.ConnectionInfo } : null); JsonSerializerSettings val = new JsonSerializerSettings { Context = new StreamingContext(StreamingContextStates.Other, additional) }; return JsonConvert.DeserializeObject<SerializableItemInfo>(json, val); } [OnDeserialized] internal void OnDeserializedMethod(StreamingContext streamingContext) { if (base.ItemGame == null && base.LocationGame != null) { IsScout = false; } else if (base.ItemGame != null && base.LocationGame == null) { IsScout = true; } if (streamingContext.Context is ItemInfoStreamingContext itemInfoStreamingContext) { if (IsScout && base.LocationGame == null) { base.LocationGame = itemInfoStreamingContext.ConnectionInfo.Game; } else if (!IsScout && base.ItemGame == null) { base.ItemGame = itemInfoStreamingContext.ConnectionInfo.Game; } if (ItemName == null) { ItemName = itemInfoStreamingContext.Items.GetItemName(base.ItemId, base.ItemGame); } if (LocationName == null) { LocationName = itemInfoStreamingContext.Locations.GetLocationNameFromId(base.LocationId, base.LocationGame); } if (Player == null) { Player = itemInfoStreamingContext.PlayerHelper.GetPlayerInfo(base.PlayerSlot); } } } } internal class ItemInfoStreamingContext { public IReceivedItemsHelper Items { get; set; } public ILocationCheckHelper Locations { get; set; } public IPlayerHelper PlayerHelper { get; set; } public IConnectionInfoProvider ConnectionInfo { get; set; } } } namespace Archipelago.MultiClient.Net.MessageLog.Parts { public class EntranceMessagePart : MessagePart { internal EntranceMessagePart(JsonMessagePart messagePart) : base(MessagePartType.Entrance, messagePart, Color.Blue) { base.Text = messagePart.Text; } } public class ItemMessagePart : MessagePart { public ItemFlags Flags { get; } public long ItemId { get; } public int Player { get; } internal ItemMessagePart(IPlayerHelper players, IItemInfoResolver items, JsonMessagePart part) : base(MessagePartType.Item, part) { Flags = part.Flags.GetValueOrDefault(); base.Color = GetColor(Flags); Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.ItemId: ItemId = long.Parse(part.Text); base.Text = items.GetItemName(ItemId, game) ?? $"Item: {ItemId}"; break; case JsonMessagePartType.ItemName: ItemId = 0L; base.Text = part.Text; break; } } } private static Color GetColor(ItemFlags flags) { if (HasFlag(flags, ItemFlags.Advancement)) { return Color.Plum; } if (HasFlag(flags, ItemFlags.NeverExclude)) { return Color.SlateBlue; } if (HasFlag(flags, ItemFlags.Trap)) { return Color.Salmon; } return Color.Cyan; } private static bool HasFlag(ItemFlags flags, ItemFlags flag) { return flags.HasFlag(flag); } } public class LocationMessagePart : MessagePart { public long LocationId { get; } public int Player { get; } internal LocationMessagePart(IPlayerHelper players, IItemInfoResolver itemInfoResolver, JsonMessagePart part) : base(MessagePartType.Location, part, Color.Green) { Player = part.Player.GetValueOrDefault(); string game = (players.GetPlayerInfo(Player) ?? new PlayerInfo()).Game; JsonMessagePartType? type = part.Type; if (type.HasValue) { switch (type.GetValueOrDefault()) { case JsonMessagePartType.LocationId: LocationId = long.Parse(part.Text); base.Text = itemInfoResolver.GetLocationName(LocationId, game) ?? $"Location: {LocationId}"; break; case JsonMessagePartType.LocationName: LocationId = itemInfoResolver.GetLocationId(part.Text, game); base.Text = part.Text; break; } } } } public class MessagePart { public string Text { get; internal set; } public MessagePartType Type { get; internal set; } public Color Color { get; internal set; } public bool IsBackgroundColor { get; internal set; } internal MessagePart(MessagePartType type, JsonMessagePart messagePart, Color? color = null) { Type = type; Text = messagePart.Text; if (color.HasValue) { Color = color.Value; } else if (messagePart.Color.HasValue) { Color = GetColor(messagePart.Color.Value); IsBackgroundColor = messagePart.Color.Value >= JsonMessagePartColor.BlackBg; } else { Color = Color.White; } } private static Color GetColor(JsonMessagePartColor color) { switch (color) { case JsonMessagePartColor.Red: case JsonMessagePartColor.RedBg: return Color.Red; case JsonMessagePartColor.Green: case JsonMessagePartColor.GreenBg: return Color.Green; case JsonMessagePartColor.Yellow: case JsonMessagePartColor.YellowBg: return Color.Yellow; case JsonMessagePartColor.Blue: case JsonMessagePartColor.BlueBg: return Color.Blue; case JsonMessagePartColor.Magenta: case JsonMessagePartColor.MagentaBg: return Color.Magenta; case JsonMessagePartColor.Cyan: case JsonMessagePartColor.CyanBg: return Color.Cyan; case JsonMessagePartColor.Black: case JsonMessagePartColor.BlackBg: return Color.Black; case JsonMessagePartColor.White: case JsonMessagePartColor.WhiteBg: return Color.White; default: return Color.White; } } public override string ToString() { return Text; } } public enum MessagePartType { Text, Player, Item, Location, Entrance } public class PlayerMessagePart : MessagePart { public bool IsActivePlayer { get; } public int SlotId { get; } internal PlayerMessagePart(IPlayerHelper players, IConnectionInfoProvider connectionInfo, JsonMessagePart part) : base(MessagePartType.Player, part) { switch (part.Type) { case JsonMessagePartType.PlayerId: SlotId = int.Parse(part.Text); IsActivePlayer = SlotId == connectionInfo.Slot; base.Text = players.GetPlayerAlias(SlotId) ?? $"Player {SlotId}"; break; case JsonMessagePartType.PlayerName: SlotId = 0; IsActivePlayer = false; base.Text = part.Text; break; } base.Color = GetColor(IsActivePlayer); } private static Color GetColor(bool isActivePlayer) { if (isActivePlayer) { return Color.Magenta; } return Color.Yellow; } } } namespace Archipelago.MultiClient.Net.MessageLog.Messages { public class AdminCommandResultLogMessage : LogMessage { internal AdminCommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class ChatLogMessage : PlayerSpecificLogMessage { public string Message { get; } internal ChatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string message) : base(parts, players, team, slot) { Message = message; } } public class CollectLogMessage : PlayerSpecificLogMessage { internal CollectLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class CommandResultLogMessage : LogMessage { internal CommandResultLogMessage(MessagePart[] parts) : base(parts) { } } public class CountdownLogMessage : LogMessage { public int RemainingSeconds { get; } internal CountdownLogMessage(MessagePart[] parts, int remainingSeconds) : base(parts) { RemainingSeconds = remainingSeconds; } } public class GoalLogMessage : PlayerSpecificLogMessage { internal GoalLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class HintItemSendLogMessage : ItemSendLogMessage { public bool IsFound { get; } internal HintItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, bool found, IItemInfoResolver itemInfoResolver) : base(parts, players, receiver, sender, item, itemInfoResolver) { IsFound = found; } } public class ItemCheatLogMessage : ItemSendLogMessage { internal ItemCheatLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, NetworkItem item, IItemInfoResolver itemInfoResolver) : base(parts, players, slot, 0, item, team, itemInfoResolver) { } } public class ItemSendLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Receiver { get; } public PlayerInfo Sender { get; } public bool IsReceiverTheActivePlayer => Receiver == ActivePlayer; public bool IsSenderTheActivePlayer => Sender == ActivePlayer; public bool IsRelatedToActivePlayer { get { if (!ActivePlayer.IsRelatedTo(Receiver)) { return ActivePlayer.IsRelatedTo(Sender); } return true; } } public ItemInfo Item { get; } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, IItemInfoResolver itemInfoResolver) : this(parts, players, receiver, sender, item, players.ActivePlayer.Team, itemInfoResolver) { } internal ItemSendLogMessage(MessagePart[] parts, IPlayerHelper players, int receiver, int sender, NetworkItem item, int team, IItemInfoResolver itemInfoResolver) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Receiver = players.GetPlayerInfo(team, receiver) ?? new PlayerInfo(); Sender = players.GetPlayerInfo(team, sender) ?? new PlayerInfo(); PlayerInfo player = players.GetPlayerInfo(team, item.Player) ?? new PlayerInfo(); Item = new ItemInfo(item, Receiver.Game, Sender.Game, itemInfoResolver, player); } } public class JoinLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal JoinLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class LeaveLogMessage : PlayerSpecificLogMessage { internal LeaveLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class LogMessage { public MessagePart[] Parts { get; } internal LogMessage(MessagePart[] parts) { Parts = parts; } public override string ToString() { if (Parts.Length == 1) { return Parts[0].Text; } StringBuilder stringBuilder = new StringBuilder(); MessagePart[] parts = Parts; foreach (MessagePart messagePart in parts) { stringBuilder.Append(messagePart.Text); } return stringBuilder.ToString(); } } public abstract class PlayerSpecificLogMessage : LogMessage { private PlayerInfo ActivePlayer { get; } public PlayerInfo Player { get; } public bool IsActivePlayer => Player == ActivePlayer; public bool IsRelatedToActivePlayer => ActivePlayer.IsRelatedTo(Player); internal PlayerSpecificLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts) { ActivePlayer = players.ActivePlayer ?? new PlayerInfo(); Player = players.GetPlayerInfo(team, slot) ?? new PlayerInfo(); } } public class ReleaseLogMessage : PlayerSpecificLogMessage { internal ReleaseLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot) : base(parts, players, team, slot) { } } public class ServerChatLogMessage : LogMessage { public string Message { get; } internal ServerChatLogMessage(MessagePart[] parts, string message) : base(parts) { Message = message; } } public class TagsChangedLogMessage : PlayerSpecificLogMessage { public string[] Tags { get; } internal TagsChangedLogMessage(MessagePart[] parts, IPlayerHelper players, int team, int slot, string[] tags) : base(parts, players, team, slot) { Tags = tags; } } public class TutorialLogMessage : LogMessage { internal TutorialLogMessage(MessagePart[] parts) : base(parts) { } } } namespace Archipelago.MultiClient.Net.Helpers { public class ArchipelagoSocketHelper : BaseArchipelagoSocketHelper<ClientWebSocket>, IArchipelagoSocketHelper { public Uri Uri { get; } internal ArchipelagoSocketHelper(Uri hostUri) : base(CreateWebSocket(), 1024) { Uri = hostUri; } private static ClientWebSocket CreateWebSocket() { return new ClientWebSocket(); } public async Task ConnectAsync() { await ConnectToProvidedUri(Uri); StartPolling(); } private async Task ConnectToProvidedUri(Uri uri) { if (uri.Scheme != "unspecified") { try { await Socket.ConnectAsync(uri, CancellationToken.None); return; } catch (Exception e) { OnError(e); throw; } } List<Exception> errors = new List<Exception>(0); try { await Socket.ConnectAsync(uri.AsWss(), CancellationToken.None); if (Socket.State == WebSocketState.Open) { return; } } catch (Exception item) { errors.Add(item); Socket = CreateWebSocket(); } try { await Socket.ConnectAsync(uri.AsWs(), CancellationToken.None); } catch (Exception item2) { errors.Add(item2); OnError(new AggregateException(errors)); throw; } } } public class BaseArchipelagoSocketHelper<T> where T : WebSocket { private static readonly ArchipelagoPacketConverter Converter = new ArchipelagoPacketConverter(); private readonly BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>> sendQueue = new BlockingCollection<Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>>(); internal T Socket; private readonly int bufferSize; public bool Connected { get { if (Socket.State != WebSocketState.Open) { return Socket.State == WebSocketState.CloseReceived; } return true; } } public event ArchipelagoSocketHelperDelagates.PacketReceivedHandler PacketReceived; public event ArchipelagoSocketHelperDelagates.PacketsSentHandler PacketsSent; public event ArchipelagoSocketHelperDelagates.ErrorReceivedHandler ErrorReceived; public event ArchipelagoSocketHelperDelagates.SocketClosedHandler SocketClosed; public event ArchipelagoSocketHelperDelagates.SocketOpenedHandler SocketOpened; internal BaseArchipelagoSocketHelper(T socket, int bufferSize = 1024) { Socket = socket; this.bufferSize = bufferSize; } internal void StartPolling() { if (this.SocketOpened != null) { this.SocketOpened(); } Task.Run((Func<Task?>)PollingLoop); Task.Run((Func<Task?>)SendLoop); } private async Task PollingLoop() { byte[] buffer = new byte[bufferSize]; while (Socket.State == WebSocketState.Open) { string message = null; try { message = await ReadMessageAsync(buffer); } catch (Exception e) { OnError(e); } OnMessageReceived(message); await Task.Delay(20); } } private async Task SendLoop() { while (Socket.State == WebSocketState.Open) { try { await HandleSendBuffer(); } catch (Exception e) { OnError(e); } await Task.Delay(20); } } private async Task<string> ReadMessageAsync(byte[] buffer) { using MemoryStream readStream = new MemoryStream(buffer.Length); WebSocketReceiveResult result; do { result = await Socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { try { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } catch { } OnSocketClosed(); } else { readStream.Write(buffer, 0, result.Count); } } while (!result.EndOfMessage); return Encoding.UTF8.GetString(readStream.ToArray()); } public async Task DisconnectAsync() { await Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closure requested by client", CancellationToken.None); OnSocketClosed(); } public void SendPacket(ArchipelagoPacketBase packet) { SendMultiplePackets(new List<ArchipelagoPacketBase> { packet }); } public void SendMultiplePackets(List<ArchipelagoPacketBase> packets) { SendMultiplePackets(packets.ToArray()); } public void SendMultiplePackets(params ArchipelagoPacketBase[] packets) { SendMultiplePacketsAsync(packets).Wait(); } public Task SendPacketAsync(ArchipelagoPacketBase packet) { return SendMultiplePacketsAsync(new List<ArchipelagoPacketBase> { packet }); } public Task SendMultiplePacketsAsync(List<ArchipelagoPacketBase> packets) { return SendMultiplePacketsAsync(packets.ToArray()); } public Task SendMultiplePacketsAsync(params ArchipelagoPacketBase[] packets) { TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(); foreach (ArchipelagoPacketBase item in packets) { sendQueue.Add(new Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>>(item, taskCompletionSource)); } return taskCompletionSource.Task; } private async Task HandleSendBuffer() { List<ArchipelagoPacketBase> list = new List<ArchipelagoPacketBase>(); List<TaskCompletionSource<bool>> tasks = new List<TaskCompletionSource<bool>>(); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> tuple = sendQueue.Take(); list.Add(tuple.Item1); tasks.Add(tuple.Item2); Tuple<ArchipelagoPacketBase, TaskCompletionSource<bool>> item; while (sendQueue.TryTake(out item)) { list.Add(item.Item1); tasks.Add(item.Item2); } if (!list.Any()) { return; } if (Socket.State != WebSocketState.Open) { throw new ArchipelagoSocketClosedException(); } ArchipelagoPacketBase[] packets = list.ToArray(); string s = JsonConvert.SerializeObject((object)packets); byte[] messageBuffer = Encoding.UTF8.GetBytes(s); int messagesCount = (int)Math.Ceiling((double)messageBuffer.Length / (double)bufferSize); for (int i = 0; i < messagesCount; i++) { int num = bufferSize * i; int num2 = bufferSize; bool endOfMessage = i + 1 == messagesCount; if (num2 * (i + 1) > messageBuffer.Length) { num2 = messageBuffer.Length - num; } await Socket.SendAsync(new ArraySegment<byte>(messageBuffer, num, num2), WebSocketMessageType.Text, endOfMessage, CancellationToken.None); } foreach (TaskCompletionSource<bool> item2 in tasks) { item2.TrySetResult(result: true); } OnPacketSend(packets); } private void OnPacketSend(ArchipelagoPacketBase[] packets) { try { if (this.PacketsSent != null) { this.PacketsSent(packets); } } catch (Exception e) { OnError(e); } } private void OnSocketClosed() { try { if (this.SocketClosed != null) { this.SocketClosed(""); } } catch (Exception e) { OnError(e); } } private void OnMessageReceived(string message) { try { if (string.IsNullOrEmpty(message) || this.PacketReceived == null) { return; } List<ArchipelagoPacketBase> list = null; try { list = JsonConvert.DeserializeObject<List<ArchipelagoPacketBase>>(message, (JsonConverter[])(object)new JsonConverter[1] { Converter }); } catch (Exception e) { OnError(e); } if (list == null) { return; } foreach (ArchipelagoPacketBase item in list) { this.PacketReceived(item); } } catch (Exception e2) { OnError(e2); } } protected void OnError(Exception e) { try { if (this.ErrorReceived != null) { this.ErrorReceived(e, e.Message); } } catch (Exception ex) { Console.Out.WriteLine("Error occured during reporting of errorOuter Errror: " + e.Message + " " + e.StackTrace + "Inner Errror: " + ex.Message + " " + ex.StackTrace); } } } public interface IConnectionInfoProvider { string Game { get; } int Team { get; } int Slot { get; } string[] Tags { get; } ItemsHandlingFlags ItemsHandlingFlags { get; } string Uuid { get; } void UpdateConnectionOptions(string[] tags); void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags); void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags); } public class ConnectionInfoHelper : IConnectionInfoProvider { private readonly IArchipelagoSocketHelper socket; public string Game { get; private set; } public int Team { get; private set; } public int Slot { get; private set; } public string[] Tags { get; internal set; } public ItemsHandlingFlags ItemsHandlingFlags { get; internal set; } public string Uuid { get; private set; } internal ConnectionInfoHelper(IArchipelagoSocketHelper socket) { this.socket = socket; Reset(); socket.PacketReceived += PacketReceived; } private void PacketReceived(ArchipelagoPacketBase packet) { if (!(packet is ConnectedPacket connectedPacket)) { if (packet is ConnectionRefusedPacket) { Reset(); } return; } Team = connectedPacket.Team; Slot = connectedPacket.Slot; if (connectedPacket.SlotInfo != null && connectedPacket.SlotInfo.ContainsKey(Slot)) { Game = connectedPacket.SlotInfo[Slot].Game; } } internal void SetConnectionParameters(string game, string[] tags, ItemsHandlingFlags itemsHandlingFlags, string uuid) { Game = game; Tags = tags ?? new string[0]; ItemsHandlingFlags = itemsHandlingFlags; Uuid = uuid ?? Guid.NewGuid().ToString(); } private void Reset() { Game = null; Team = -1; Slot = -1; Tags = new string[0]; ItemsHandlingFlags = ItemsHandlingFlags.NoItems; Uuid = null; } public void UpdateConnectionOptions(string[] tags) { UpdateConnectionOptions(tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(ItemsHandlingFlags itemsHandlingFlags) { UpdateConnectionOptions(Tags, ItemsHandlingFlags); } public void UpdateConnectionOptions(string[] tags, ItemsHandlingFlags itemsHandlingFlags) { SetConnectionParameters(Game, tags, itemsHandlingFlags, Uuid); socket.SendPacket(new ConnectUpdatePacket { Tags = Tags, ItemsHandling = ItemsHandlingFlags }); } } public interface IDataStorageHelper : IDataStorageWrapper { DataStorageElement this[Scope scope, string key] { get; set; } DataStorageElement this[string key] { get; set; } } public class DataStorageHelper : IDataStorageHelper, IDataStorageWrapper { public delegate void DataStorageUpdatedHandler(JToken originalValue, JToken newValue, Dictionary<string, JToken> additionalArguments); private readonly Dictionary<string, DataStorageUpdatedHandler> onValueChangedEventHandlers = new Dictionary<string, DataStorageUpdatedHandler>(); private readonly Dictionary<Guid, DataStorageUpdatedHandler> operationSpecificCallbacks = new Dictionary<Guid, DataStorageUpdatedHandler>(); private readonly Dictionary<string, TaskCompletionSource<JToken>> asyncRetrievalTasks = new Dictionary<string, TaskCompletionSource<JToken>>(); private readonly IArchipelagoSocketHelper socket; private readonly IConnectionInfoProvider connectionInfoProvider; public DataStorageElement this[Scope scope, string key] { get { return this[AddScope(scope, key)]; } set { this[AddScope(scope, key)] = value; } } public DataStorageElement this[string key] { get { return new DataStorageElement(GetContextForKey(key)); } set { SetValue(key, value); } } internal DataStorageHelper(IArchipelagoSocketHelper socket, IConnectionInfoProvider connectionInfoProvider) { this.socket = socket; this.connectionInfoProvider = connectionInfoProvider; socket.PacketReceived += OnPacketReceived; } private void OnPacketReceived(ArchipelagoPacketBase packet) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Invalid comparison between Unknown and I4 if (!(packet is RetrievedPacket retrievedPacket)) { if (packet is SetReplyPacket setReplyPacket) { if (setReplyPacket.AdditionalArguments != null && setReplyPacket.AdditionalArguments.ContainsKey("Reference") && (int)setReplyPacket.AdditionalArguments["Reference"].Type == 15 && operationSpecificCallbacks.TryGetValue((Guid)setReplyPacket.AdditionalArguments["Reference"], out var value)) { value(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments); operationSpecificCallbacks.Remove((Guid)setReplyPacket.AdditionalArguments["Reference"]); } if (onValueChangedEventHandlers.TryGetValue(setReplyPacket.Key, out var value2)) { value2(setReplyPacket.OriginalValue, setReplyPacket.Value, setReplyPacket.AdditionalArguments); } } return; } foreach (KeyValuePair<string, JToken> datum in retrievedPacket.Data) { if (asyncRetrievalTasks.TryGetValue(datum.Key, out var value3)) { value3.TrySetResult(datum.Value); asyncRetrievalTasks.Remove(datum.Key); } } } private Task<JToken> GetAsync(string key) { if (asyncRetrievalTasks.TryGetValue(key, out var value)) { return value.Task; } TaskCompletionSource<JToken> taskCompletionSource = new TaskCompletionSource<JToken>(); asyncRetrievalTasks[key] = taskCompletionSource; socket.SendPacketAsync(new GetPacket { Keys = new string[1] { key } }); return taskCompletionSource.Task; } private void Initialize(string key, JToken value) { socket.SendPacketAsync(new SetPacket { Key = key, DefaultValue = value, Operations = new OperationSpecification[1] { new OperationSpecification { OperationType = OperationType.Default } } }); } private JToken GetValue(string key) { Task<JToken> async = GetAsync(key); if (!async.Wait(TimeSpan.FromSeconds(2.0))) { throw new TimeoutException("Timed out retrieving data for key `" + key + "`. This may be due to an attempt to retrieve a value from the DataStorageHelper in a synchronous fashion from within a PacketReceived handler. When using the DataStorageHelper from within code which runs on the websocket thread then use the asynchronous getters. Ex: `DataStorageHelper[\"" + key + "\"].GetAsync().ContinueWith(x => {});`Be aware that DataStorageHelper calls tend to cause packet responses, so making a call from within a PacketReceived handler may cause an infinite loop."); } return async.Result; } private void SetValue(string key, DataStorageElement e) { if (key.StartsWith("_read_")) { throw new InvalidOperationException("DataStorage write operation on readonly key '" + key + "' is not allowed"); } if (e == null) { e = new DataStorageElement(OperationType.Replace, (JToken)(object)JValue.CreateNull()); } if (e.Context == null) { e.Context = GetContextForKey(key); } else if (e.Context.Key != key) { e.Operations.Insert(0, new OperationSpecification { OperationType = OperationType.Replace, Value = GetValue(e.Context.Key) }); } Dictionary<string, JToken> dictionary = e.AdditionalArguments ?? new Dictionary<string, JToken>(0); if (e.Callbacks != null) { Guid guid = Guid.NewGuid(); operationSpecificCallbacks[guid] = e.Callbacks; dictionary["Reference"] = JToken.FromObject((object)guid); socket.SendPacketAsync(new SetPacket { Key = key, Operations = e.Operations.ToArray(), WantReply = true, AdditionalArguments = dictionary }); } else { socket.SendPacketAsync(new SetPacket { Key = key, Operations = e.Operations.ToArray(), AdditionalArguments = dictionary }); } } private DataStorageElementContext GetContextForKey(string key) { return new DataStorageElementContext { Key = key, GetData = GetValue, GetAsync = GetAsync, Initialize = Initialize, AddHandler = AddHandler, RemoveHandler = RemoveHandler }; } private void AddHandler(string key, DataStorageUpdatedHandler handler) { if (onValueChangedEventHandlers.ContainsKey(key)) { Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers; dictionary[key] = (DataStorageUpdatedHandler)Delegate.Combine(dictionary[key], handler); } else { onValueChangedEventHandlers[key] = handler; } socket.SendPacketAsync(new SetNotifyPacket { Keys = new string[1] { key } }); } private void RemoveHandler(string key, DataStorageUpdatedHandler handler) { if (onValueChangedEventHandlers.ContainsKey(key)) { Dictionary<string, DataStorageUpdatedHandler> dictionary = onValueChangedEventHandlers; dictionary[key] = (DataStorageUpdatedHandler)Delegate.Remove(dictionary[key], handler); if (onValueChangedEventHandlers[key] == null) { onValueChangedEventHandlers.Remove(key); } } } private string AddScope(Scope scope, string key) { return scope switch { Scope.Global => key, Scope.Game => $"{scope}:{connectionInfoProvider.Game}:{key}", Scope.Team => $"{scope}:{connectionInfoProvider.Team}:{key}", Scope.Slot => $"{scope}:{connectionInfoProvider.Slot}:{key}", Scope.ReadOnly => "_read_" + key, _ => throw new ArgumentOutOfRangeException("scope", scope, "Invalid scope for key " + key), }; } private DataStorageElement GetHintsElement(int? slot = null, int? team = null) { return this[Scope.ReadOnly, $"hints_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"]; } private DataStorageElement GetSlotDataElement(int? slot = null) { return this[Scope.ReadOnly, $"slot_data_{slot ?? connectionInfoProvider.Slot}"]; } private DataStorageElement GetItemNameGroupsElement(string game = null) { return this[Scope.ReadOnly, "item_name_groups_" + (game ?? connectionInfoProvider.Game)]; } private DataStorageElement GetLocationNameGroupsElement(string game = null) { return this[Scope.ReadOnly, "location_name_groups_" + (game ?? connectionInfoProvider.Game)]; } private DataStorageElement GetClientStatusElement(int? slot = null, int? team = null) { return this[Scope.ReadOnly, $"client_status_{team ?? connectionInfoProvider.Team}_{slot ?? connectionInfoProvider.Slot}"]; } public Hint[] GetHints(int? slot = null, int? team = null) { return GetHintsElement(slot, team).To<Hint[]>(); } public Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null) { return GetHintsElement(slot, team).GetAsync<Hint[]>(); } public void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlockedHints = true, int? slot = null, int? team = null) { GetHintsElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onHintsUpdated(newValue.ToObject<Hint[]>()); }; if (retrieveCurrentlyUnlockedHints) { GetHintsAsync(slot, team).ContinueWith(delegate(Task<Hint[]> t) { onHintsUpdated(t.Result); }); } } public Dictionary<string, object> GetSlotData(int? slot = null) { return GetSlotData<Dictionary<string, object>>(slot); } public T GetSlotData<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).To<T>(); } public Task<Dictionary<string, object>> GetSlotDataAsync(int? slot = null) { return GetSlotDataAsync<Dictionary<string, object>>(slot); } public Task<T> GetSlotDataAsync<T>(int? slot = null) where T : class { return GetSlotDataElement(slot).GetAsync<T>(); } public Dictionary<string, string[]> GetItemNameGroups(string game = null) { return GetItemNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetItemNameGroupsAsync(string game = null) { return GetItemNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public Dictionary<string, string[]> GetLocationNameGroups(string game = null) { return GetLocationNameGroupsElement(game).To<Dictionary<string, string[]>>(); } public Task<Dictionary<string, string[]>> GetLocationNameGroupsAsync(string game = null) { return GetLocationNameGroupsElement(game).GetAsync<Dictionary<string, string[]>>(); } public ArchipelagoClientState GetClientStatus(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).To<ArchipelagoClientState?>().GetValueOrDefault(); } public Task<ArchipelagoClientState> GetClientStatusAsync(int? slot = null, int? team = null) { return GetClientStatusElement(slot, team).GetAsync<ArchipelagoClientState?>().ContinueWith((Task<ArchipelagoClientState?> r) => r.Result.GetValueOrDefault()); } public void TrackClientStatus(Action<ArchipelagoClientState> onStatusUpdated, bool retrieveCurrentClientStatus = true, int? slot = null, int? team = null) { GetClientStatusElement(slot, team).OnValueChanged += delegate(JToken _, JToken newValue, Dictionary<string, JToken> x) { onStatusUpdated(newValue.ToObject<ArchipelagoClientState>()); }; if (retrieveCurrentClientStatus) { GetClientStatusAsync(slot, team).ContinueWith(delegate(Task<ArchipelagoClientState> t) { onStatusUpdated(t.Result); }); } } } public interface IDataStorageWrapper { Hint[] GetHints(int? slot = null, int? team = null); Task<Hint[]> GetHintsAsync(int? slot = null, int? team = null); void TrackHints(Action<Hint[]> onHintsUpdated, bool retrieveCurrentlyUnlock
ArchipelagoRandomizer.dll
Decompiled 15 minutes 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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using Archipelago.MultiClient.Net; using Archipelago.MultiClient.Net.BounceFeatures.DeathLink; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Helpers; using Archipelago.MultiClient.Net.MessageLog.Messages; using Archipelago.MultiClient.Net.MessageLog.Parts; using Archipelago.MultiClient.Net.Models; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Cysharp.Threading.Tasks; using HarmonyLib; using I2.Loc; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NineSolsAPI; using RCGFSM.Items; using RCGFSM.Map; using RCGFSM.PlayerAction; using RCGFSM.Variable; using RCGMaker.AddressableAssets; using RCGMaker.Core; using RCGMaker.Runtime; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [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("ArchipelagoRandomizer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A Nine Sols mod for the Archipelago multi-game randomizer system")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f3eefed1ec269d57d202e999cca2a98bb1d2d79e")] [assembly: AssemblyProduct("ArchipelagoRandomizer")] [assembly: AssemblyTitle("ArchipelagoRandomizer")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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 ArchipelagoRandomizer { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("ArchipelagoRandomizer", "ArchipelagoRandomizer", "1.0.0")] public class APRandomizer : BaseUnityPlugin { private ConfigEntry<bool> DeathLinkSetting; private Harmony harmony; public static string SaveSlotsPath => Application.persistentDataPath; private void Awake() { //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) Log.Init(((BaseUnityPlugin)this).Logger); RCGLifeCycle.DontDestroyForever(((Component)this).gameObject); harmony = Harmony.CreateAndPatchAll(typeof(APRandomizer).Assembly, (string)null); DeathLinkSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("", "Death Link", false, "When you die, everyone who enabled death link dies. Of course, the reverse is true too."); Log.Info($"applying initial DeathLink setting of {DeathLinkSetting.Value}"); DeathLinkManager.ApplyModSetting(DeathLinkSetting.Value); DeathLinkSetting.SettingChanged += delegate { Log.Info($"DeathLink setting changed to {DeathLinkSetting.Value}"); DeathLinkManager.ApplyModSetting(DeathLinkSetting.Value); }; Log.Info("trying to load Archipelago item and location IDs"); try { Assembly assembly = typeof(APRandomizer).GetTypeInfo().Assembly; using (StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream("ArchipelagoRandomizer.items.jsonc"))) { ItemNames.LoadArchipelagoIds(streamReader.ReadToEnd()); } using (StreamReader streamReader2 = new StreamReader(assembly.GetManifestResourceStream("ArchipelagoRandomizer.locations.jsonc"))) { LocationNames.LoadArchipelagoIds(streamReader2.ReadToEnd()); } Log.Info("loaded Archipelago item and location IDs"); } catch (Exception ex) { Log.Warning("id loading threw: " + ex.Message + " with stack:\n" + ex.StackTrace); if (ex.InnerException != null) { Log.Warning("id loading threw inner: " + ex.InnerException.Message + " with stack:\n" + ex.InnerException.StackTrace); } } Action obj = delegate { DebugTools.ShowDebugToolsPopup = !DebugTools.ShowDebugToolsPopup; }; KeyCode[] array = new KeyCode[3]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); KeybindManager.Add((MonoBehaviour)(object)this, obj, new KeyboardShortcut((KeyCode)100, (KeyCode[])(object)array)); Action obj2 = delegate { for (int i = 0; i < 30; i++) { Log.Info(""); } }; KeyCode[] array2 = new KeyCode[3]; RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); KeybindManager.Add((MonoBehaviour)(object)this, obj2, new KeyboardShortcut((KeyCode)120, (KeyCode[])(object)array2)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin ArchipelagoRandomizer is loaded!"); } private void OnDestroy() { harmony.UnpatchSelf(); } private void Update() { ConnectionAndPopups.Update(); DebugTools.Update(); } private void OnGUI() { ConnectionAndPopups.OnGUI(); DebugTools.OnGUI(); } } [HarmonyPatch] public class DeathLinkManager { [CompilerGenerated] private static class <>O { public static DeathLinkReceivedHandler <0>__OnDeathLinkReceived; } public static bool DeathLinkSettingValue = false; private static DeathLinkService service = null; private static bool manualDeathInProgress = false; private static List<string> deathMessages = new List<string> { " became one with the Tao.", " wasn't smoking enough.", " could not parry death." }; private static Random prng = new Random(); public static void ApplyModSetting(bool enabled) { DeathLinkSettingValue = enabled; if (DeathLinkSettingValue && service == null && ConnectionAndPopups.APSession != null) { CreateDeathLinkService(); return; } ConnectionAndPopups.OnSessionOpened += delegate { if (DeathLinkSettingValue && service == null) { CreateDeathLinkService(); } }; } private static void CreateDeathLinkService() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (service != null) { return; } if (ConnectionAndPopups.APSession == null) { Log.Error("EnableDeathLinkImplHelper unable to create death link service because APSession is null"); return; } service = DeathLinkProvider.CreateDeathLinkService(ConnectionAndPopups.APSession); service.EnableDeathLink(); DeathLinkService obj = service; object obj2 = <>O.<0>__OnDeathLinkReceived; if (obj2 == null) { DeathLinkReceivedHandler val = OnDeathLinkReceived; <>O.<0>__OnDeathLinkReceived = val; obj2 = (object)val; } obj.OnDeathLinkReceived += (DeathLinkReceivedHandler)obj2; } public static void OnDeathLinkReceived(DeathLink deathLinkObject) { Log.Info($"OnDeathLinkReceived() Timestamp={deathLinkObject.Timestamp}, Source={deathLinkObject.Source}, Cause={deathLinkObject.Cause}"); ToastManager.Toast((object)deathLinkObject.Cause); ActuallyKillThePlayer(); } private static void ActuallyKillThePlayer() { manualDeathInProgress = true; Player.i.health.ReceiveDOT_Damage(9999f); manualDeathInProgress = false; } [HarmonyPrefix] [HarmonyPatch(typeof(GameLevel), "HandlePlayerKilled")] public static void GameLevel_HandlePlayerKilled(GameLevel __instance) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown Log.Info("GameLevel.HandlePlayerKilled called in " + ((Object)__instance).name); if (manualDeathInProgress) { Log.Info("GameLevel.HandlePlayerKilled patch ignoring death because this is a death we received from another player"); return; } if (!DeathLinkSettingValue) { Log.Info("GameLevel.HandlePlayerKilled patch ignoring death since death_link is off"); return; } if (service == null) { Log.Error("Unable to send death to AP server because death link service is null"); return; } Log.Info("GameLevel.HandlePlayerKilled detected a death, sending to AP server"); string slotName = APSaveManager.CurrentAPSaveData.apConnectionData.slotName; string text = slotName + deathMessages[prng.Next(0, deathMessages.Count)]; ToastManager.Toast((object)("Because death link is enabled, sending this death to other players with the message: \"" + text + "\"")); service.SendDeathLink(new DeathLink(slotName, text)); } } internal class DebugTools { public static bool ShowDebugToolsPopup = false; private static string DebugPopup_Item = ""; private static string DebugPopup_Count = ""; public static void Update() { if (ShowDebugToolsPopup) { Cursor.visible = true; } } public static void OnGUI() { ConnectionAndPopups.UpdateStyles(); if (ShowDebugToolsPopup) { DrawDebugToolsPopup(); } } private static void DrawDebugToolsPopup() { //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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) float num = (float)Screen.width * 0.6f; float num2 = (float)Screen.height * 0.7f; Rect windowRect = new Rect(((float)Screen.width - num) / 2f, ((float)Screen.height - num2) / 2f, num, num2); GUILayout.Width(((Rect)(ref windowRect)).width * 0.6f); GUIStyle windowStyle = ConnectionAndPopups.windowStyle; GUIStyle labelStyle = ConnectionAndPopups.labelStyle; GUIStyle textFieldStyle = ConnectionAndPopups.textFieldStyle; GUIStyle buttonStyle = ConnectionAndPopups.buttonStyle; GUIStyle centeredLabelStyle = new GUIStyle(labelStyle); centeredLabelStyle.alignment = (TextAnchor)1; GUI.Window(11261728, windowRect, (WindowFunction)delegate { //IL_00ba: 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_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Expected O, but got Unknown //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Expected O, but got Unknown //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_049a: Expected O, but got Unknown GUILayout.Label("", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("NPCs & Events", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Unlock Jiequan 1 Fight", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"unlocking Jiequan 1 Fight"); Jiequan1Fight.ActuallyTriggerJiequan1Fight(); } if (GUILayout.Button("Unlock Lady E Soulscape", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"unlocking Lady E Soulscape"); LadyESoulscapeEntrance.ActuallyTriggerLadyESoulscape(); } if (GUILayout.Button("Move Chiyou into FSP", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"moving Chiyou into FSP"); ((ScriptableDataBool)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["bf49eb7e251013c4cb62eca6e586b465ScriptableDataBool"]).CurrentValue = true; } if (GUILayout.Button("Move Kuafu into FSP", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"moving Kuafu into FSP"); ((ScriptableDataBool)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["e2ccc29dc8f187b45be6ce50e7f4174aScriptableDataBool"]).CurrentValue = true; } GUILayout.EndHorizontal(); GUILayout.Label("", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Miscellaneous", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Unlock Most Teleports", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"unlocking most teleport points"); NewGameCreation.UnlockAllTeleportPoints(); } if (GUILayout.Button("Test Death Link", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"triggering test death link"); DeathLinkManager.OnDeathLinkReceived(new DeathLink("death link test player", "death link test cause")); } if (GUILayout.Button("Give 9999 Jin", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"giving 99999 jin"); SingletonBehaviour<GameCore>.Instance.playerGameData.AddGold(99999, (GoldSourceTag)4); } if (GUILayout.Button("Check 1 Unchecked Location", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"triggering random unchecked location check"); long key = ConnectionAndPopups.APSession.Locations.AllMissingLocations[0]; LocationTriggers.CheckLocation(LocationNames.archipelagoIdToLocation[key]); } GUILayout.EndHorizontal(); GUILayout.Label("", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Core Progression Items", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUIStyle val = new GUIStyle(labelStyle); val.fixedWidth = 200f; GUIStyle val2 = new GUIStyle(buttonStyle); val2.fixedWidth = 50f; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("MysticNymphScoutMode", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.MysticNymphScoutMode, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.MysticNymphScoutMode, 0); } GUILayout.Label("TaiChiKick", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.TaiChiKick, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.TaiChiKick, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("ChargedStrike", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.ChargedStrike, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.ChargedStrike, 0); } GUILayout.Label("AirDash", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.AirDash, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.AirDash, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("UnboundedCounter", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.UnboundedCounter, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.UnboundedCounter, 0); } GUILayout.Label("CloudLeap", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.CloudLeap, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.CloudLeap, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("SuperMutantBuster", val, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.SuperMutantBuster, 1); } if (GUILayout.Button("Off", val2, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.SuperMutantBuster, 0); } GUILayout.EndHorizontal(); GUIStyle val3 = new GUIStyle(buttonStyle); val3.fixedWidth = 80f; GUILayout.Label("", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Arbitrary Item Update", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DebugPopup_Item = GUILayout.TextField(DebugPopup_Item, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(((Rect)(ref windowRect)).width * 0.3f) }); DebugPopup_Count = GUILayout.TextField(DebugPopup_Count, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(((Rect)(ref windowRect)).width * 0.1f) }); if (GUILayout.Button("Update", val3, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Enum.Parse<Item>(DebugPopup_Item), int.Parse(DebugPopup_Count)); } GUILayout.EndHorizontal(); }, "Archipelago Randomizer Debug Tools (Ctrl+Alt+Shift+D to show/hide)", windowStyle); } } [HarmonyPatch] internal class Goal { [HarmonyPrefix] [HarmonyPatch(typeof(SetVariableBoolAction), "OnStateEnterImplement")] private static void SetVariableBoolAction_OnStateEnterImplement(SetVariableBoolAction __instance) { object obj; if (__instance == null) { obj = null; } else { VariableBool targetFlag = __instance.targetFlag; if (targetFlag == null) { obj = null; } else { ScriptableDataBool boolFlag = targetFlag.boolFlag; obj = ((boolFlag != null) ? ((GameFlagBase)boolFlag).FinalSaveID : null); } } string text = (string)obj; if (text != null) { bool flag = false; if (text == "85d361b9-7c43-4a1d-91c2-fd19e4bbb0b1_6b037c39982d34953a066792ab66783aScriptableDataBool") { flag = true; } else if (text == "e965aab1c014b4273b928b17fbcff379ScriptableDataBool") { flag = true; } if (flag) { Log.Info($"Eigong flag: id={text}, TargetValue={__instance.TargetValue}"); ToastManager.Toast((object)"Eigong defeat detected by SetVariableBoolAction_OnStateEnterImplement. Congratulations!"); ToastManager.Toast((object)"Telling the AP server that you've achieved your goal."); ConnectionAndPopups.APSession.SetGoalAchieved(); } } } } [HarmonyPatch] internal class ItemApplications { private static readonly object itemReceivedLock = new object(); public static readonly Dictionary<Item, int> ApInventory = new Dictionary<Item, int>(); public static HashSet<(Item, int)> deferredUpdates = new HashSet<(Item, int)>(); private static string[] shopCUs = new string[4] { "7e52ba8ef1da6e946806ba1791d92791PlayerAbilityData", "5df7e883274aabc489885525a490b5c3PlayerAbilityData", "d5b834eb32c40e64f9ce89f5033162c8PlayerAbilityData", "d6dc79cb7147ff14aa68f5658a285f22PlayerAbilityData" }; private static string[] otherCUs = new string[4] { "07577e5ef6cbe394db65f58dfc8f7908PlayerAbilityData", "df57aee56d7eec1459ffe946cac8523ePlayerAbilityData", "cd52a0bd3cef5634bb0083c19c77e8f3PlayerAbilityData", "c191c1bc2afb8d84c870e1858b7ee156PlayerAbilityData" }; private static MethodInfo padActivate = AccessTools.Method(typeof(PlayerAbilityData), "Activate", Array.Empty<Type>(), (Type[])null); private static MethodInfo padDeactivate = AccessTools.Method(typeof(PlayerAbilityData), "DeActivate", Array.Empty<Type>(), (Type[])null); private static string[] shopPVs = new string[4] { "5c864e36f2cef9d4f8c24c0aa84010bePlayerAbilityData", "3f6040257515a41499d36e910a4a6e79PlayerAbilityData", "ec1fe3b64944cd643b2020f35e82f023PlayerAbilityData", "dd5adfbe0ac50fe4795b796153ee646dPlayerAbilityData" }; private static string[] otherPVs = new string[2] { "ce7166af4ef39d7468c4ccc464fd90b9PlayerAbilityData", "41c960dfcaaf7a14f813342db16f0481PlayerAbilityData" }; public static void ItemReceived(IReceivedItemsHelper receivedItemsHelper) { try { HashSet<long> hashSet = new HashSet<long>(); while (receivedItemsHelper.PeekItem() != null) { long itemId = receivedItemsHelper.PeekItem().ItemId; hashSet.Add(itemId); receivedItemsHelper.DequeueItem(); } lock (itemReceivedLock) { Log.Info("ItemReceived event with item ids " + string.Join(", ", hashSet) + ". Updating these item counts."); foreach (long item in hashSet) { SyncItemCountWithAPServer(item); } } APSaveManager.ScheduleWriteToCurrentSaveFile(); } catch (Exception ex) { Log.Error("Caught error in APSession_ItemReceived: '" + ex.Message + "'\n" + ex.StackTrace); } } public static void LoadSavedInventory(APRandomizerSaveData apSaveData) { foreach (var (value, value2) in apSaveData.itemsAcquired) { ApInventory[Enum.Parse<Item>(value)] = value2; } } public static void SyncInventoryWithServer() { int num = ApInventory.Sum((KeyValuePair<Item, int> kv) => kv.Value); int count = ConnectionAndPopups.APSession.Items.AllItemsReceived.Count; if (count <= num) { return; } Log.Info($"AP server state has more items ({count}) than local save data ({num}). Attempting to apply new items."); foreach (ItemInfo item in ConnectionAndPopups.APSession.Items.AllItemsReceived) { SyncItemCountWithAPServer(item.ItemId); } } public static void SyncItemCountWithAPServer(long itemId) { if (!ItemNames.archipelagoIdToItem.ContainsKey(itemId)) { ToastManager.Toast((object)($"<color=red>Warning</color>: This mod does not recognize the item id {itemId}, which the Archipelago server just sent us. " + "Check if your mod version matches the .apworld version used to generate this multiworld.")); return; } Item item = ItemNames.archipelagoIdToItem[itemId]; string key = item.ToString(); APRandomizerSaveData currentAPSaveData = APSaveManager.CurrentAPSaveData; if (!currentAPSaveData.itemsAcquired.ContainsKey(key)) { currentAPSaveData.itemsAcquired[key] = 0; } int num = ConnectionAndPopups.APSession.Items.AllItemsReceived.Where((ItemInfo i) => i.ItemId == itemId).Count(); if (currentAPSaveData.itemsAcquired[key] < num) { UpdateItemCount(item, num); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameCore), "InitializeGameLevel")] private static async void GameLevel_InitializeGameCore_Postfix(GameCore __instance, GameLevel newLevel) { if (deferredUpdates.Count <= 0) { return; } Log.Info($"GameLevel_InitializeGameCore_Postfix has {deferredUpdates.Count} deferredUpdates to execute. Waiting for base game fade-in to finish."); await UniTask.Delay(1000, false, (PlayerLoopTiming)8, default(CancellationToken)); foreach (var (item, count) in deferredUpdates) { UpdateItemCount(item, count); } deferredUpdates.Clear(); APSaveManager.WriteCurrentSaveFile(); } public static void UpdateItemCount(Item item, int count) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) Log.Info($"UpdateItemCount(item={item}, count={count})"); if (!SingletonBehaviour<GameCore>.IsAvailable()) { Log.Info("UpdateItemCount deferring this item update since we aren't in the game yet"); deferredUpdates.Add((item, count)); return; } int valueOrDefault = ApInventory.GetValueOrDefault(item); GameFlagDescriptable item2 = ApplyItemToPlayer(item, count, valueOrDefault).Item1; if ((Object)(object)item2 != (Object)null && count > valueOrDefault) { SingletonBehaviour<UIManager>.Instance.ShowDescriptableNitification(item2); SingletonBehaviour<SaveManager>.Instance.AutoSave((SaveSceneScheme)0, true, (Transform)null); } ApInventory[item] = count; Jiequan1Fight.OnItemUpdate(item); LadyESoulscapeEntrance.OnItemUpdate(item); if (APSaveManager.CurrentAPSaveData == null) { Log.Error($"UpdateItemCount(item={item}, count={count}) unable to write to save file because there is no save file. If you're the developer doing hot reloading, this is normal."); } else { APSaveManager.CurrentAPSaveData.itemsAcquired[item.ToString()] = count; } } public static bool IsSolSeal(Item item) { if (item >= Item.SealOfKuafu) { return item <= Item.SealOfNuwa; } return false; } public static int GetSolSealsCount() { int num = 0; for (Item item = Item.SealOfKuafu; item <= Item.SealOfNuwa; item++) { if (ApInventory.ContainsKey(item) && ApInventory[item] > 0) { num++; } } return num; } private static (GameFlagDescriptable?, bool) ApplyItemToPlayer(Item item, int count, int oldCount) { //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0986: Unknown result type (might be due to invalid IL or missing references) //IL_09db: Unknown result type (might be due to invalid IL or missing references) //IL_0a8f: Unknown result type (might be due to invalid IL or missing references) //IL_10b0: Unknown result type (might be due to invalid IL or missing references) //IL_10b5: Unknown result type (might be due to invalid IL or missing references) //IL_10db: Expected O, but got Unknown Log.Info($"ApplyItemToPlayer(item={item}, count={count}, oldCount={oldCount})"); PlayerAbilityData val = null; switch (item) { case Item.TaiChiKick: val = Player.i.mainAbilities.ParryJumpKickAbility; break; case Item.AirDash: val = Player.i.mainAbilities.RollDodgeInAirUpgrade; break; case Item.ChargedStrike: val = Player.i.mainAbilities.ChargedAttackAbility; break; case Item.CloudLeap: val = Player.i.mainAbilities.AirJumpAbility; break; case Item.SuperMutantBuster: val = Player.i.mainAbilities.KillZombieFooAbility; break; case Item.UnboundedCounter: val = Player.i.mainAbilities.ParryCounterAbility; break; case Item.MysticNymphScoutMode: val = Player.i.mainAbilities.HackDroneAbility; break; } if ((Object)(object)val != (Object)null) { ((FlagField<bool>)(object)((GameFlagDescriptable)val).acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)val.equipped).SetCurrentValue(count > 0, (Object)null); if ((Object)(object)val.BindingItemPicked != (Object)null) { Log.Info($"!!! {val} has BindingItemPicked={val.BindingItemPicked} !!!"); } return ((GameFlagDescriptable)(object)val, true); } List<ItemDataCollection> allItemCollections = SingletonBehaviour<UIManager>.Instance.allItemCollections; GameFlagDescriptable val2 = null; switch (item) { case Item.SealOfKuafu: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[0]; break; case Item.SealOfGoumang: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[1]; break; case Item.SealOfYanlao: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[2]; break; case Item.SealOfJiequan: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[3]; break; case Item.SealOfLadyEthereal: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[4]; break; case Item.SealOfJi: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[5]; break; case Item.SealOfFuxi: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[6]; break; case Item.SealOfNuwa: val2 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[7]; break; } if ((Object)(object)val2 != (Object)null) { ((FlagField<bool>)(object)val2.acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)val2.unlocked).SetCurrentValue(count > 0, (Object)null); ((FlagField<int>)(object)((ItemData)val2).ownNum).SetCurrentValue(count, (Object)null); return (val2, true); } GameFlagDescriptable val3 = null; switch (item) { case Item.BloodyCrimsonHibiscus: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[8]; break; case Item.AncientPenglaiBallad: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[9]; break; case Item.PoemHiddenInTheImmortalsPortrait: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[10]; break; case Item.ThunderburstBomb: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[11]; break; case Item.GeneEradicator: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[12]; break; case Item.HomingDarts: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[13]; break; case Item.SoulSeveringBlade: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[14]; break; case Item.FirestormRing: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[15]; break; case Item.JisHair: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[18]; break; case Item.TianhuoSerum: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[19]; break; case Item.ElevatorAccessToken: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[20]; break; case Item.RhizomaticBomb: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[21]; break; case Item.AbandonedMinesAccessToken: val3 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[23]; break; case Item.AbandonedMinesChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[0]; break; case Item.PowerReservoirChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[1]; break; case Item.AgriculturalZoneChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[2]; break; case Item.WarehouseZoneChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[3]; break; case Item.TransmutationZoneChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[4]; break; case Item.CentralCoreChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[5]; break; case Item.EmpyreanDistrictChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[6]; break; case Item.GrottoOfScripturesChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[7]; break; case Item.ResearchCenterChip: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[8]; break; case Item.FusangAmulet: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[9]; break; case Item.MultiToolKit: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[10]; break; case Item.TiandaoAcademyPeriodical: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[11]; break; case Item.KunlunImmortalPortrait: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[12]; break; case Item.QiankunBoard: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[13]; break; case Item.AncientSheetMusic: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[14]; break; case Item.UnknownSeed: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[15]; break; case Item.VirtualRealityDevice: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[16]; break; case Item.AntiqueVinylRecord: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[17]; break; case Item.ReadyToEatRations: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[18]; break; case Item.SwordOfJie: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[19]; break; case Item.RedGuifangClay: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[20]; break; case Item.TheFourTreasuresOfTheStudy: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[21]; break; case Item.GMFertilizer: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[23]; break; case Item.PenglaiRecipeCollection: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[24]; break; case Item.MedicinalCitrine: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[25]; break; case Item.GoldenYinglongEgg: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[26]; break; case Item.ResidualHair: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[28]; break; case Item.Oriander: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[29]; break; case Item.TurtleScorpion: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[30]; break; case Item.PlantagoFrog: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[31]; break; case Item.PorcineGem: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[32]; break; case Item.LegendOfThePorkyHeroes: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[41]; break; case Item.PortraitOfYi: val3 = ((AbstractGameFlagCollection)allItemCollections[1]).rawCollection[42]; break; case Item.BasicComponent: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[0]; break; case Item.StandardComponent: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[1]; break; case Item.AdvancedComponent: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[2]; break; case Item.NobleRing: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[3]; break; case Item.PassengerTokenAShou: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[6]; break; case Item.PassengerTokenZouyan: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[7]; break; case Item.PassengerTokenXipu: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[8]; break; case Item.PassengerTokenJihai: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[9]; break; case Item.PassengerTokenYangfan: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[10]; break; case Item.PassengerTokenAimu: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[11]; break; case Item.PassengerTokenShiyangyue: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[12]; break; case Item.DarkSteel: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[13]; break; case Item.HerbCatalyst: val3 = ((AbstractGameFlagCollection)allItemCollections[2]).rawCollection[14]; break; } if ((Object)(object)val3 != (Object)null) { ((FlagField<bool>)(object)val3.acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)val3.unlocked).SetCurrentValue(count > 0, (Object)null); if (val3 is ItemData) { int num = count - oldCount; int currentValue = ((FlagField<int>)(object)((ItemData)val3).ownNum).CurrentValue; int num2 = currentValue + num; Log.Info($"{item} apCountDiff={num}, oldInGameCount={currentValue}, newInGameCount={num2}"); ((FlagField<int>)(object)((ItemData)val3).ownNum).SetCurrentValue(num2, (Object)null); } return (val3, true); } GameFlagDescriptable val4 = null; int num3 = 0; switch (item) { case Item.GreaterTaoFruit: val4 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[25]; num3 = 2; break; case Item.TaoFruit: val4 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[26]; num3 = 1; break; case Item.TwinTaoFruit: val4 = ((AbstractGameFlagCollection)allItemCollections[0]).rawCollection[27]; num3 = 4; break; } if ((Object)(object)val4 != (Object)null) { ((FlagField<bool>)(object)val4.acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)val4.unlocked).SetCurrentValue(count > 0, (Object)null); ((FlagField<int>)(object)((ItemData)val4).ownNum).SetCurrentValue(count, (Object)null); int num4 = count - oldCount; if (num4 > 0) { int num5 = num4 * num3; Log.Info($"Giving the player {num5} skill points for the {num4} new '{val4.Title}'s they received"); PlayerGamePlayData playerGameData = SingletonBehaviour<GameCore>.Instance.playerGameData; playerGameData.SkillPointLeft += num5; } return (val4, true); } List<GameFlagDescriptable> rawCollection = ((AbstractGameFlagCollection)SingletonBehaviour<UIManager>.Instance.allPediaCollections[2]).rawCollection; GameFlagDescriptable val5 = null; switch (item) { case Item.DeadPersonsNote: val5 = rawCollection[0]; break; case Item.CampScroll: val5 = rawCollection[1]; break; case Item.ApemanSurveillanceFootage: val5 = rawCollection[2]; break; case Item.CouncilDigitalSignage: val5 = rawCollection[3]; break; case Item.NewKunlunLaunchMemorial: val5 = rawCollection[4]; break; case Item.AnomalousRootNode: val5 = rawCollection[5]; break; case Item.RhizomaticEnergyMeter: val5 = rawCollection[6]; break; case Item.DuskGuardianRecordingDevice1: val5 = rawCollection[7]; break; case Item.RadiantPagodaControlPanel: val5 = rawCollection[8]; break; case Item.LakeYaochiStele: val5 = rawCollection[9]; break; case Item.AncientCavePainting: val5 = rawCollection[10]; break; case Item.CoffinInscription: val5 = rawCollection[11]; break; case Item.YellowWaterReport: val5 = rawCollection[12]; break; case Item.MutatedCrops: val5 = rawCollection[13]; break; case Item.WaterSynthesisPipelinePanel: val5 = rawCollection[14]; break; case Item.DuskGuardianRecordingDevice2: val5 = rawCollection[15]; break; case Item.FarmlandMarkings: val5 = rawCollection[16]; break; case Item.CouncilTenets: val5 = rawCollection[17]; break; case Item.TransmutationFurnaceMonitor: val5 = rawCollection[18]; break; case Item.EvacuationNoticeForMiners: val5 = rawCollection[19]; break; case Item.WarehouseDatabase: val5 = rawCollection[20]; break; case Item.DuskGuardianRecordingDevice3: val5 = rawCollection[21]; break; case Item.AncientWeaponConsole: val5 = rawCollection[22]; break; case Item.HexachremVaultScroll: val5 = rawCollection[23]; break; case Item.PrisonersBambooScroll1: val5 = rawCollection[24]; break; case Item.PrisonersBambooScroll2: val5 = rawCollection[25]; break; case Item.JieClanFamilyPrecept: val5 = rawCollection[26]; break; case Item.GuardProductionStation: val5 = rawCollection[27]; break; case Item.DuskGuardianRecordingDevice4: val5 = rawCollection[28]; break; case Item.PharmacyPanel: val5 = rawCollection[29]; break; case Item.HaotianSphereModel: val5 = rawCollection[30]; break; case Item.CaveStoneInscription: val5 = rawCollection[31]; break; case Item.GalacticDockSign: val5 = rawCollection[32]; break; case Item.StoneCarvings: val5 = rawCollection[33]; break; case Item.SecretMural1: val5 = rawCollection[34]; break; case Item.SecretMural2: val5 = rawCollection[35]; break; case Item.SecretMural3: val5 = rawCollection[36]; break; case Item.StowawaysCorpse: val5 = rawCollection[37]; break; case Item.UndergroundWaterTower: val5 = rawCollection[38]; break; case Item.EmpyreanBulletinBoard: val5 = rawCollection[39]; break; case Item.DuskGuardianRecordingDevice5: val5 = rawCollection[40]; break; case Item.VitalSanctumTowerMonitoringPanel: val5 = rawCollection[41]; break; case Item.DuskGuardianRecordingDevice6: val5 = rawCollection[42]; break; case Item.DuskGuardianHeadquartersScreen: val5 = rawCollection[43]; break; } if ((Object)(object)val5 != (Object)null) { ((FlagField<bool>)(object)val5.acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)val5.unlocked).SetCurrentValue(count > 0, (Object)null); return (val5, true); } List<JadeData> gameFlagDataList = ((GameFlagBaseCollection<JadeData>)(object)Player.i.mainAbilities.jadeDataColleciton).gameFlagDataList; JadeData val6 = null; switch (item) { case Item.ImmovableJade: val6 = gameFlagDataList[0]; break; case Item.HarnessForceJade: val6 = gameFlagDataList[1]; break; case Item.FocusJade: val6 = gameFlagDataList[2]; break; case Item.SwiftDescentJade: val6 = gameFlagDataList[3]; break; case Item.SteelyJade: val6 = gameFlagDataList[6]; break; case Item.StasisJade: val6 = gameFlagDataList[7]; break; case Item.MobQuellJadeYin: val6 = gameFlagDataList[8]; break; case Item.BearingJade: val6 = gameFlagDataList[10]; break; case Item.DivineHandJade: val6 = gameFlagDataList[11]; break; case Item.IronSkinJade: val6 = gameFlagDataList[12]; break; case Item.PauperJade: val6 = gameFlagDataList[13]; break; case Item.SwiftBladeJade: val6 = gameFlagDataList[14]; break; case Item.BreatherJade: val6 = gameFlagDataList[17]; break; case Item.HedgehogJade: val6 = gameFlagDataList[18]; break; case Item.RevivalJade: val6 = gameFlagDataList[20]; break; case Item.SoulReaperJade: val6 = gameFlagDataList[21]; break; case Item.QiBladeJade: val6 = gameFlagDataList[23]; break; case Item.QiSwipeJade: val6 = gameFlagDataList[24]; break; case Item.CultivationJade: val6 = gameFlagDataList[26]; break; case Item.AvariceJade: val6 = gameFlagDataList[27]; break; } if ((Object)(object)val6 != (Object)null) { ((FlagField<bool>)(object)((GameFlagDescriptable)val6).acquired).SetCurrentValue(count > 0, (Object)null); ((FlagField<bool>)(object)((GameFlagDescriptable)val6).unlocked).SetCurrentValue(count > 0, (Object)null); return ((GameFlagDescriptable)(object)val6, true); } string text = null; switch (item) { case Item.ArrowCloudPiercer: text = "7837bd6bb550641d8a9f30492603c5eePlayerWeaponData"; break; case Item.ArrowShadowHunter: text = "3949bc0edba197d459f5d2d7f15c72e0PlayerWeaponData"; break; case Item.ArrowThunderBuster: text = "ef8f7eb3bcd7b444f80d5da539f3b133PlayerWeaponData"; break; } if (text != null) { PlayerWeaponData val7 = (PlayerWeaponData)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict[text]; ((FlagField<bool>)(object)((GameFlagDescriptable)val7).acquired)?.SetCurrentValue(count > 0, (Object)null); if (count > 0) { EnableAzureBow(enable: true); } return ((GameFlagDescriptable)val7, true); } int num6 = 0; switch (item) { case Item.Jin800: num6 = 800; break; case Item.Jin320: num6 = 320; break; case Item.Jin50: num6 = 50; break; } if (num6 != 0) { int num7 = count - oldCount; if (num7 > 0) { int num8 = num7 * num6; SingletonBehaviour<GameCore>.Instance.playerGameData.AddGold(num8, (GoldSourceTag)1); } return (((AbstractGameFlagCollection)SingletonBehaviour<UIManager>.Instance.allItemCollections[3]).rawCollection[1], false); } switch (item) { case Item.ComputingUnit: return ApplyComputingUnit(count); case Item.PipeVial: return ApplyPipeVial(count); default: ToastManager.Toast((object)$"unable to apply item {item} (count = {count})"); return (null, false); } } private static void EnableAzureBow(bool enable) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown Log.Info($"EnableAzureBow(enable={enable})"); ((FlagField<bool>)(object)((GameFlagDescriptable)Player.i.mainAbilities.ArrowAbility).acquired)?.SetCurrentValue(enable, (Object)null); ItemData val = (ItemData)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["49f2fd2c691313f47970b15b58279418ItemData"]; ItemData val2 = (ItemData)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["25b45e1c416880d41a1f1444e45c24d2ItemData"]; ItemData val3 = (ItemData)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["a68fe303d0077264aa66218d3900f0edItemData"]; ItemData[] array = (ItemData[])(object)new ItemData[3] { val, val2, val3 }; foreach (ItemData obj in array) { ((FlagField<bool>)(object)((GameFlagDescriptable)obj).acquired)?.SetCurrentValue(enable, (Object)null); ((FlagField<bool>)(object)((GameFlagDescriptable)obj).unlocked)?.SetCurrentValue(enable, (Object)null); } } private static (GameFlagDescriptable?, bool) ApplyComputingUnit(int apCount) { Log.Info($"ApplyComputingUnit(apCount={apCount})"); string[] source = shopCUs; string[] array = otherCUs; int num = array.Length; if (apCount < 0 || apCount > num) { Log.Error($"ApplyComputingUnit passed {apCount}, but apCount must be between 0 and (on this slot) {num}"); return (null, false); } Dictionary<string, GameFlagBase> flagDict = SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict; for (int i = 0; i < array.Length; i++) { string key = array[i]; GameFlagBase obj = flagDict[key]; PlayerAbilityData val = (PlayerAbilityData)(object)((obj is PlayerAbilityData) ? obj : null); ((FlagField<bool>)(object)((GameFlagDescriptable)val).unlocked).CurrentValue = i < apCount; ((FlagField<bool>)(object)((GameFlagDescriptable)val).acquired).CurrentValue = i < apCount; if (i < apCount) { padActivate.Invoke(val, Array.Empty<object>()); } else { padDeactivate.Invoke(val, Array.Empty<object>()); } } int num2 = source.Sum(delegate(string flagId) { GameFlagBase obj3 = flagDict[flagId]; return ((FlagField<bool>)(object)((GameFlagDescriptable)((obj3 is PlayerAbilityData) ? obj3 : null)).acquired).CurrentValue ? 1 : 0; }) + apCount; GameFlagDescriptable obj2 = ((AbstractGameFlagCollection)SingletonBehaviour<UIManager>.Instance.allItemCollections[3]).rawCollection[6]; ((FlagField<bool>)(object)obj2.unlocked).CurrentValue = num2 > 0; ((FlagField<bool>)(object)obj2.acquired).CurrentValue = num2 > 0; ((FlagField<int>)(object)((ItemData)((obj2 is ItemData) ? obj2 : null)).ownNum).CurrentValue = num2; return (obj2, true); } private static (GameFlagDescriptable?, bool) ApplyPipeVial(int apCount) { Log.Info($"ApplyPipeVial(apCount={apCount})"); string[] source = shopPVs; string[] array = otherPVs; int num = array.Length; if (apCount < 0 || apCount > num) { Log.Error($"ApplyPipeVial passed {apCount}, but apCount must be between 0 and (on this slot) {num}"); return (null, false); } Dictionary<string, GameFlagBase> flagDict = SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict; for (int i = 0; i < array.Length; i++) { string key = array[i]; GameFlagBase obj = flagDict[key]; PlayerAbilityData val = (PlayerAbilityData)(object)((obj is PlayerAbilityData) ? obj : null); ((FlagField<bool>)(object)((GameFlagDescriptable)val).unlocked).CurrentValue = i < apCount; ((FlagField<bool>)(object)((GameFlagDescriptable)val).acquired).CurrentValue = i < apCount; if (i < apCount) { padActivate.Invoke(val, Array.Empty<object>()); } else { padDeactivate.Invoke(val, Array.Empty<object>()); } } int num2 = source.Sum(delegate(string flagId) { GameFlagBase obj3 = flagDict[flagId]; return ((FlagField<bool>)(object)((GameFlagDescriptable)((obj3 is PlayerAbilityData) ? obj3 : null)).acquired).CurrentValue ? 1 : 0; }) + apCount; GameFlagDescriptable obj2 = ((AbstractGameFlagCollection)SingletonBehaviour<UIManager>.Instance.allItemCollections[3]).rawCollection[10]; ((FlagField<bool>)(object)obj2.unlocked).CurrentValue = num2 > 0; ((FlagField<bool>)(object)obj2.acquired).CurrentValue = num2 > 0; ((FlagField<int>)(object)((ItemData)((obj2 is ItemData) ? obj2 : null)).ownNum).CurrentValue = num2; return (obj2, true); } } public enum Item { SealOfKuafu, SealOfGoumang, SealOfYanlao, SealOfJiequan, SealOfLadyEthereal, SealOfJi, SealOfFuxi, SealOfNuwa, MysticNymphScoutMode, TaiChiKick, ChargedStrike, AirDash, UnboundedCounter, CloudLeap, SuperMutantBuster, ArrowCloudPiercer, ArrowThunderBuster, ArrowShadowHunter, HerbCatalyst, PipeVial, TaoFruit, GreaterTaoFruit, TwinTaoFruit, ComputingUnit, DarkSteel, AbandonedMinesAccessToken, LegendOfThePorkyHeroes, JisHair, TianhuoSerum, ElevatorAccessToken, RhizomaticBomb, HomingDarts, ThunderburstBomb, FusangAmulet, MultiToolKit, AncientSheetMusic, UnknownSeed, GMFertilizer, SwordOfJie, AntiqueVinylRecord, QiankunBoard, RedGuifangClay, PenglaiRecipeCollection, TiandaoAcademyPeriodical, KunlunImmortalPortrait, VirtualRealityDevice, ReadyToEatRations, TheFourTreasuresOfTheStudy, BloodyCrimsonHibiscus, AncientPenglaiBallad, PortraitOfYi, PoemHiddenInTheImmortalsPortrait, SoulSeveringBlade, FirestormRing, NobleRing, PassengerTokenZouyan, PassengerTokenAShou, PassengerTokenXipu, PassengerTokenYangfan, PassengerTokenJihai, PassengerTokenAimu, PassengerTokenShiyangyue, GeneEradicator, CentralCoreChip, PowerReservoirChip, AgriculturalZoneChip, AbandonedMinesChip, WarehouseZoneChip, TransmutationZoneChip, GrottoOfScripturesChip, EmpyreanDistrictChip, ResearchCenterChip, StasisJade, BearingJade, HarnessForceJade, IronSkinJade, HedgehogJade, PauperJade, SteelyJade, ImmovableJade, SoulReaperJade, AvariceJade, RevivalJade, SwiftDescentJade, MobQuellJadeYin, MobQuellJadeYang, FocusJade, SwiftBladeJade, BreatherJade, QiSwipeJade, QiBladeJade, DivineHandJade, CultivationJade, MedicinalCitrine, GoldenYinglongEgg, ResidualHair, PorcineGem, PlantagoFrog, Oriander, TurtleScorpion, ApemanSurveillanceFootage, CouncilDigitalSignage, NewKunlunLaunchMemorial, CouncilTenets, AnomalousRootNode, RhizomaticEnergyMeter, RadiantPagodaControlPanel, DuskGuardianRecordingDevice1, LakeYaochiStele, YellowWaterReport, MutatedCrops, DuskGuardianRecordingDevice2, WaterSynthesisPipelinePanel, JieClanFamilyPrecept, TransmutationFurnaceMonitor, DuskGuardianRecordingDevice4, GuardProductionStation, CaveStoneInscription, DeadPersonsNote, CampScroll, WarehouseDatabase, DuskGuardianRecordingDevice3, AncientWeaponConsole, HexachremVaultScroll, AncientCavePainting, CoffinInscription, StoneCarvings, SecretMural1, SecretMural2, SecretMural3, StowawaysCorpse, EmpyreanBulletinBoard, DuskGuardianRecordingDevice5, VitalSanctumTowerMonitoringPanel, DuskGuardianRecordingDevice6, DuskGuardianHeadquartersScreen, FarmlandMarkings, EvacuationNoticeForMiners, PrisonersBambooScroll1, PrisonersBambooScroll2, PharmacyPanel, HaotianSphereModel, GalacticDockSign, UndergroundWaterTower, Jin800, Jin320, Jin50, BasicComponent, StandardComponent, AdvancedComponent } internal class ItemNames { public static Dictionary<Item, string> itemNames = new Dictionary<Item, string> { { Item.SealOfKuafu, "Seal of Kuafu" }, { Item.SealOfGoumang, "Seal of Goumang" }, { Item.SealOfYanlao, "Seal of Yanlao" }, { Item.SealOfJiequan, "Seal of Jiequan" }, { Item.SealOfLadyEthereal, "Seal of Lady Ethereal" }, { Item.SealOfJi, "Seal of Ji" }, { Item.SealOfFuxi, "Seal of Fuxi" }, { Item.SealOfNuwa, "Seal of Nuwa" }, { Item.MysticNymphScoutMode, "Mystic Nymph: Scout Mode" }, { Item.TaiChiKick, "Tai-Chi Kick" }, { Item.ChargedStrike, "Charged Strike" }, { Item.AirDash, "Air Dash" }, { Item.UnboundedCounter, "Unbounded Counter" }, { Item.CloudLeap, "Cloud Leap" }, { Item.SuperMutantBuster, "Super Mutant Buster" }, { Item.ArrowCloudPiercer, "Arrow: Cloud Piercer" }, { Item.ArrowThunderBuster, "Arrow: Thunder Buster" }, { Item.ArrowShadowHunter, "Arrow: Shadow Hunter" }, { Item.HerbCatalyst, "Herb Catalyst" }, { Item.PipeVial, "Pipe Vial" }, { Item.TaoFruit, "Tao Fruit" }, { Item.GreaterTaoFruit, "Greater Tao Fruit" }, { Item.TwinTaoFruit, "Twin Tao Fruit" }, { Item.ComputingUnit, "Computing Unit" }, { Item.DarkSteel, "Dark Steel" }, { Item.AbandonedMinesAccessToken, "Abandoned Mines Access Token" }, { Item.LegendOfThePorkyHeroes, "(Artifact) Legend of the Porky Heroes" }, { Item.JisHair, "Ji's Hair" }, { Item.TianhuoSerum, "Tianhuo Serum" }, { Item.ElevatorAccessToken, "Elevator Access Token" }, { Item.RhizomaticBomb, "Rhizomatic Bomb" }, { Item.HomingDarts, "Homing Darts" }, { Item.ThunderburstBomb, "Thunderburst Bomb" }, { Item.FusangAmulet, "(Artifact) Fusang Amulet" }, { Item.MultiToolKit, "(Artifact) Multi-tool Kit" }, { Item.AncientSheetMusic, "(Artifact) Ancient Sheet Music" }, { Item.UnknownSeed, "(Artifact) Unknown Seed" }, { Item.GMFertilizer, "(Artifact) GM Fertilizer" }, { Item.SwordOfJie, "(Artifact) Sword of Jie" }, { Item.AntiqueVinylRecord, "(Artifact) Antique Vinyl Record" }, { Item.QiankunBoard, "(Artifact) Qiankun Board" }, { Item.RedGuifangClay, "(Artifact) Red Guifang Clay" }, { Item.PenglaiRecipeCollection, "(Artifact) Penglai Recipe Collection" }, { Item.TiandaoAcademyPeriodical, "(Artifact) Tiandao Academy Periodical" }, { Item.KunlunImmortalPortrait, "(Artifact) Kunlun Immortal Portrait" }, { Item.VirtualRealityDevice, "(Artifact) Virtual Reality Device" }, { Item.ReadyToEatRations, "(Artifact) Ready-to-Eat Rations" }, { Item.TheFourTreasuresOfTheStudy, "(Artifact) The Four Treasures of the Study" }, { Item.BloodyCrimsonHibiscus, "Bloody Crimson Hibiscus" }, { Item.AncientPenglaiBallad, "Ancient Penglai Ballad" }, { Item.PortraitOfYi, "(Artifact) Portrait of Yi" }, { Item.PoemHiddenInTheImmortalsPortrait, "Poem Hidden in the Immortal's Portrait" }, { Item.SoulSeveringBlade, "Soul-Severing Blade" }, { Item.FirestormRing, "Firestorm Ring" }, { Item.NobleRing, "(Recyclable) Noble Ring" }, { Item.PassengerTokenZouyan, "(Recyclable) Passenger Token: Zouyan" }, { Item.PassengerTokenAShou, "(Recyclable) Passenger Token: A-Shou" }, { Item.PassengerTokenXipu, "(Recyclable) Passenger Token: Xipu" }, { Item.PassengerTokenYangfan, "(Recyclable) Passenger Token: Yangfan" }, { Item.PassengerTokenJihai, "(Recyclable) Passenger Token: Jihai" }, { Item.PassengerTokenAimu, "(Recyclable) Passenger Token: Aimu" }, { Item.PassengerTokenShiyangyue, "(Recyclable) Passenger Token: Shiyangyue" }, { Item.GeneEradicator, "Gene Eradicator" }, { Item.CentralCoreChip, "Central Core Chip" }, { Item.PowerReservoirChip, "Power Reservoir Chip" }, { Item.AgriculturalZoneChip, "Agricultural Zone Chip" }, { Item.AbandonedMinesChip, "Abandoned Mines Chip" }, { Item.WarehouseZoneChip, "Warehouse Zone Chip" }, { Item.TransmutationZoneChip, "Transmutation Zone Chip" }, { Item.GrottoOfScripturesChip, "Grotto of Scriptures Chip" }, { Item.EmpyreanDistrictChip, "Empyrean District Chip" }, { Item.ResearchCenterChip, "Research Center Chip" }, { Item.StasisJade, "Stasis Jade" }, { Item.BearingJade, "Bearing Jade" }, { Item.HarnessForceJade, "Harness Force Jade" }, { Item.IronSkinJade, "Iron Skin Jade" }, { Item.HedgehogJade, "Hedgehog Jade" }, { Item.PauperJade, "Pauper Jade" }, { Item.SteelyJade, "Steely Jade" }, { Item.ImmovableJade, "Immovable Jade" }, { Item.SoulReaperJade, "Soul Reaper Jade" }, { Item.AvariceJade, "Avarice Jade" }, { Item.RevivalJade, "Revival Jade" }, { Item.SwiftDescentJade, "Swift Descent Jade" }, { Item.MobQuellJadeYin, "Mob Quell Jade - Yin" }, { Item.MobQuellJadeYang, "Mob Quell Jade - Yang" }, { Item.FocusJade, "Focus Jade" }, { Item.SwiftBladeJade, "Swift Blade Jade" }, { Item.BreatherJade, "Breather Jade" }, { Item.QiSwipeJade, "Qi Swipe Jade" }, { Item.QiBladeJade, "Qi Blade Jade" }, { Item.DivineHandJade, "Divine Hand Jade" }, { Item.CultivationJade, "Cultivation Jade" }, { Item.MedicinalCitrine, "(Poison) Medicinal Citrine" }, { Item.GoldenYinglongEgg, "(Poison) Golden Yinglong Egg" }, { Item.ResidualHair, "(Poison) Residual Hair" }, { Item.PorcineGem, "(Poison) Porcine Gem" }, { Item.PlantagoFrog, "(Poison) Plantago Frog" }, { Item.Oriander, "(Poison) Oriander" }, { Item.TurtleScorpion, "(Poison) Turtle Scorpion" }, { Item.ApemanSurveillanceFootage, "(Database) Apeman Surveillance Footage" }, { Item.CouncilDigitalSignage, "(Database) Council Digital Signage" }, { Item.NewKunlunLaunchMemorial, "(Database) New Kunlun Launch Memorial" }, { Item.CouncilTenets, "(Database) Council Tenets" }, { Item.AnomalousRootNode, "(Database) Anomalous Root Node" }, { Item.RhizomaticEnergyMeter, "(Database) Rhizomatic Energy Meter" }, { Item.RadiantPagodaControlPanel, "(Database) Radiant Pagoda Control Panel" }, { Item.DuskGuardianRecordingDevice1, "(Database) Dusk Guardian Recording Device 1" }, { Item.LakeYaochiStele, "(Database) Lake Yaochi Stele" }, { Item.YellowWaterReport, "(Database) Yellow Water Report" }, { Item.MutatedCrops, "(Database) Mutated Crops" }, { Item.DuskGuardianRecordingDevice2, "(Database) Dusk Guardian Recording Device 2" }, { Item.WaterSynthesisPipelinePanel, "(Database) Water Synthesis Pipeline Panel" }, { Item.JieClanFamilyPrecept, "(Database) Jie Clan Family Precept" }, { Item.TransmutationFurnaceMonitor, "(Database) Transmutation Furnace Monitor" }, { Item.DuskGuardianRecordingDevice4, "(Database) Dusk Guardian Recording Device 4" }, { Item.GuardProductionStation, "(Database) Guard Production Station" }, { Item.CaveStoneInscription, "(Database) Cave Stone Inscription" }, { Item.DeadPersonsNote, "(Database) Dead Person's Note" }, { Item.CampScroll, "(Database) Camp Scroll" }, { Item.WarehouseDatabase, "(Database) Warehouse Database" }, { Item.DuskGuardianRecordingDevice3, "(Database) Dusk Guardian Recording Device 3" }, { Item.AncientWeaponConsole, "(Database) Ancient Weapon Console" }, { Item.HexachremVaultScroll, "(Database) Hexachrem Vault Scroll" }, { Item.AncientCavePainting, "(Database) Ancient Cave Painting" }, { Item.CoffinInscription, "(Database) Coffin Inscription" }, { Item.StoneCarvings, "(Database) Stone Carvings" }, { Item.SecretMural1, "(Database) Secret Mural I" }, { Item.SecretMural2, "(Database) Secret Mural II" }, { Item.SecretMural3, "(Database) Secret Mural III" }, { Item.StowawaysCorpse, "(Database) Stowaway's Corpse" }, { Item.EmpyreanBulletinBoard, "(Database) Empyrean Bulletin Board" }, { Item.DuskGuardianRecordingDevice5, "(Database) Dusk Guardian Recording Device 5" }, { Item.VitalSanctumTowerMonitoringPanel, "(Database) Vital Sanctum Tower Monitoring Panel" }, { Item.DuskGuardianRecordingDevice6, "(Database) Dusk Guardian Recording Device 6" }, { Item.DuskGuardianHeadquartersScreen, "(Database) Dusk Guardian Headquarters" }, { Item.FarmlandMarkings, "(Database) Farmland Markings" }, { Item.EvacuationNoticeForMiners, "(Database) Evacuation Notice For Miners" }, { Item.PrisonersBambooScroll1, "(Database) Prisoner's Bamboo Scroll I" }, { Item.PrisonersBambooScroll2, "(Database) Prisoner's Bamboo Scroll II" }, { Item.PharmacyPanel, "(Database) Pharmacy Panel" }, { Item.HaotianSphereModel, "(Database) Haotian Sphere Model" }, { Item.GalacticDockSign, "(Database) Galactic Dock Sign" }, { Item.UndergroundWaterTower, "(Database) Underground Water Tower" }, { Item.Jin800, "Jin x800" }, { Item.Jin320, "Jin x320" }, { Item.Jin50, "Jin x50" }, { Item.BasicComponent, "(Recyclable) Basic Component" }, { Item.StandardComponent, "(Recyclable) Standard Component" }, { Item.AdvancedComponent, "(Recyclable) Advanced Component" } }; public static Dictionary<string, Item> itemNamesReversed = itemNames.ToDictionary<KeyValuePair<Item, string>, string, Item>((KeyValuePair<Item, string> itemName) => itemName.Value, (KeyValuePair<Item, string> itemName) => itemName.Key); public static Dictionary<long, Item> archipelagoIdToItem = null; public static Dictionary<Item, long> itemToArchipelagoId = null; public static void LoadArchipelagoIds(string itemsFileContent) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 JArray obj = JArray.Parse(itemsFileContent); archipelagoIdToItem = new Dictionary<long, Item>(); itemToArchipelagoId = new Dictionary<Item, long>(); foreach (JToken item2 in obj) { if ((int)item2[(object)"code"].Type != 10) { long num = (long)item2[(object)"code"]; string text = (string)item2[(object)"name"]; if (!itemNamesReversed.ContainsKey(text)) { throw new Exception("LoadArchipelagoIds failed: unknown item name " + text); } Item item = itemNamesReversed[text]; archipelagoIdToItem.Add(num, item); itemToArchipelagoId.Add(item, num); } } } } public enum Location { AFM_BREAK_CORPSE, AFM_CHEST_UPPER_RIGHT, AFM_CHEST_LOWER_VENT, AFM_DB_SURVEILLANCE, AFE_CHEST_UPPER_PAGODA_LEFT, AFE_CHEST_UPPER_PAGODA_RIGHT, AFE_CHEST_MOVING_BOXES, AFE_CHEST_ELEVATOR, AFE_DROP_BAICHANG, AFE_CHEST_STATUE, AFE_CHEST_OVER_HAZARD, AFD_CHEST_CRYSTAL_CAVES, AFD_DROP_1_SHANGUI, AFD_DROP_2_SHANGUI, AFD_CHEST_BELOW_NODE, AFD_CHEST_STATUES, AFD_CHEST_LOWER_LEVEL, AFD_CHEST_UNDER_LOWER_LEFT_EXIT, AFD_FLOWER_UNDER_ELEVATOR, AFD_CHEST_UPPER_RIGHT_1, AFD_CHEST_UPPER_RIGHT_2, CH_COUNCIL_SIGN, CH_LAUNCH_MEMORIAL, CH_COUNCIL_TENETS, CH_CHEST_AXEBOT_AND_TURRETS, CH_CHEST_VENTS, FSP_KUAFU_GIFT_1, FSP_KUAFU_GIFT_2, FSP_SHUANSHUAN_MUSIC, FSP_SHUANSHUAN_PORTRAIT, FSP_CHIYOU_PORTRAIT, FSP_SHENNONG_PBV_QUEST, FSP_SHUANSHUAN_BOOK, FSP_CHIYOU_BOOK, FSP_SHUANSHUAN_HIDDEN_POEM, FSP_CHEST_HALF_TREE, FSP_CHEST_FULL_TREE_1, FSP_CHEST_FULL_TREE_2, FSP_MUTANT_QUEST, FSP_KUAFU_HOMING_DARTS, FSP_KUAFU_THUNDERBURST_BOMB, CC_LADY_ETHEREAL, CC_FLOWER_LADY_ETHEREAL, CC_SHANHAI_CHIP, CC_CHEST_CAVES_UPPER_RIGHT, CC_CHEST_CAVES_CENTER, CC_CHEST_LEFT_EXIT, CTH_CHEST_LARGE_ELEVATOR, CTH_DROP_YANREN, CTH_CHEST_SIDE_ROOM, CTH_CHEST_VENTS_LEFT, CTH_ANOMALOUS_ROOT_NODE, CTH_CHEST_STATUES, PRE_CHEST_AFTER_LASERS, PRE_CHEST_UNDER_BOX, PRE_CHEST_UPPER_RIGHT, PRE_CHEST_STATUE, PRE_CHEST_UPPER_LEFT, PRE_DROP_JIAODUAN, PRC_CHEST_BREAKABLE_WALL_RIGHT, PRC_CHEST_NEAR_MOVING_BOX, PRC_CHEST_STATUE, PRC_CHEST_GUARDED_BY_BEETLE, PRC_CHEST_RIGHT_OF_PAGODA, PRC_SHANHAI_CHIP, PRC_RHIZOMATIC_ENERGY_METER, PRC_CHEST_LEFT_EXIT, PRC_CHEST_LEFT_OF_BRIDGE, RP_CONTROL_PANEL, RP_DROP_YINGZHAO, RP_KUAFU_SANCTUM, PRW_CHEST_BELOW_NODE, PRW_CHEST_GUARDED_BY_TURRET, PRW_CHEST_VENTS, PRW_CHEST_STATUE, PRW_DGRD, PRW_FLOWER, PRW_CHEST_RIGHT_EXIT, LYR_CHEST_LEFT_POOL_MIDDLE_1, LYR_CHEST_LEFT_POOL_MIDDLE_2, LYR_CHEST_LEFT_POOL_RIGHT, LYR_CHEST_LEFT_POOL_ABOVE, LYR_JI_MUSIC, LYR_CHEST_TOWER, LYR_CHEST_TOWER_ROOM, LYR_CHEST_ABOVE_NODE, LYR_CHEST_STATUES_ROOM, LYR_LAKE_STELE, LYR_CHEST_NYMPH_ROOM, LYR_CHEST_RIGHT_EXIT, GH_CHEST_NYMPH_ROPE, GH_WATER_REPORT, GH_SHANHAI_CHIP, GH_CHEST_RIGHT_HANGING_POOL, GH_UPPER_LEVEL_FOLIAGE, GH_UNDERWATER_VASE, GH_CHEST_LEFT_HANGING_POOL, GH_MUTATED_CROPS, GH_DROP_SHUIGUI, WOS_CHEST_HIGH_PLATFORMS_RIGHT, WOS_CHEST_HIGH_PLATFORMS_LEFT, WOS_FLOWER, WOS_DGRD, WOS_PIPELINE_PANEL, WOS_SHAFT_NEAR_NODE, AH_CHEST_GOUMANG_1, AH_CHEST_GOUMANG_2, AH_GOUMANG_SANCTUM, YC_CHEST_UPPER_CAVES_TOP_LEFT, YC_CHEST_UPPER_CAVES_BOTTOM_LEFT, YC_FARMLAND_MARKINGS, YC_ABOVE_MARKINGS, YC_CHEST_MIDDLE_CAVE, YC_CAVE_EGG, YC_NEAR_NODE, FGH_CHEST_RIGHT_SHIELD_ORB, FGH_CHEST_NEAR_NODE, FGH_CHEST_MIDDLE_SHIELD_ORB, FGH_HAMMER_FLOWER, FGH_HAMMER_BROS, FGH_MONITOR, FGH_CHIYOU_BRIDGE, FGH_CHEST_ABOVE_NODE, FGH_CHEST_RIGHT_ELEVATOR, FGH_PLATFORM_ROOM_BALL, FGH_PLATFORM_ROOM_DGRD, FGH_PLATFORM_ROOM_FLOWER, FU_CHEST_UPPER_RIGHT_EXIT, FU_DROP_KUIYAN, FU_CHEST_LOWER_ELEVATOR, FU_CHEST_STATUES, FU_DROP_SHANHAI, FU_EVACUATION_NOTICE, FU_CHEST_ABOVE_NODE, FU_CHEST_BELOW_LEFT_EXIT, FU_CHEST_BEHIND_BOXES, P_SCROLL_LOWER_RIGHT, P_CHEST_LOWER_LEFT, P_CHEST_ABOVE_STAIRS, P_CHEST_NEAR_NODE, P_SCROLL_UPPER_LEFT, P_CHESTS_BEFORE_KANGHUI_1, P_CHESTS_BEFORE_KANGHUI_2, P_CHESTS_BEFORE_KANGHUI_3, P_DROP_KANGHUI, FMR_CHEST_RIGHT_ELEVATOR, FMR_CHEST_ELEVATOR_TURRET, FMR_CHEST_LEFT_ELEVATOR, FMR_CHEST_WALKING_GREEN_PILLAR, FMR_CHEST_TURRET_GREEN_PILLAR, FMR_CHEST_NEAR_MOVING_PLATFORMS, FMR_FLOWER, FPA_CHEST_VENTS_LOWER_LEFT, FPA_CHEST_NEAR_JIEQUAN_STATUE, FPA_CHEST_TRIPLE_GUARD_SPAWNER, FPA_CHEST_VENTS_UPPER_LEFT, FPA_PRODUCTION_STATION, FPA_VENTS_BELOW_PRODUCTION, FPA_CHEST_BELOW_DOUBLE_SNIPER, FPA_PHARMACY_PANEL, FPA_PHARMACY_BALL, FPA_CHEST_RIGHT_FIRE_ZONE_BOTTOM, FPA_DEFEAT_SHANHAI, FPA_RIGHT_FIRE_ZONE_TOP, FPA_CHEST_PAST_WUQIANG, FPA_DROP_WUQIANG, SH_HAOTIAN_SPHERE, SH_CHEST_RIGHT, SH_JIEQUAN_FLOWER, SH_CHEST_LEFT, SH_JIEQUAN_SANCTUM, AM_CHEST_ABOVE_LEFT_EXIT, AM_DROP_YINYUE, AM_CHEST_NEAR_NODE, AM_CHEST_WALKING, AM_FLOWER, AM_CHEST_AFTER_FLOWER_1, AM_CHEST_AFTER_FLOWER_2, UC_INSCRIPTION, UC_NOTE, UC_SCROLL, LHP_DROP_LIEGUAN, GD_SIGN, GD_FLOWER, GD_SHAMAN, OW_SHANHAI_CHIP, OW_CHEST_ABOVE_SOL_STATUE, OW_CHEST_CRUSHER_GAUNTLET, OW_CHEST_GAUNTLET_ROOM, OW_CHEST_VENTS_ROBOT, OW_DROP_VENT_CRATE, OW_DATABASE, IW_CHEST_STATUES, IW_CHEST_WALKING, IW_DROP_TIEYAN, IW_NYMPH_PUZZLE_ROOM, IW_DGRD, IW_FLOWER, BR_CONSOLE, BR_CHEST_NEAR_CONSOLE, BR_GAUNTLET_1_CHEST, BR_GAUNTLET_2_CHEST, BR_GAUNTLET_2_CHEST_LASERS, BR_GAUNTLET_2_CHEST_BEETLE, BR_VAULT_CHEST_1, BR_VAULT_CHEST_2, BR_VAULT_SCROLL, BR_VAULT_CHEST_3, BR_VAULT_CHEST_4, YH_FLOWER, YH_VITAL_SANCTUM, GOSY_PAINTING, GOSY_COFFIN, GOSY_CHEST_TREASURE_1, GOSY_CHEST_TREASURE_2, GOSY_CHEST_TREASURE_3, GOSY_CHEST_TREASURE_4, GOSY_CHEST_TREASURE_5, GOSY_CHEST_TREASURE_6, GOSY_CHEST_LOWER_PORTAL, GOSY_CHEST_CAVES_LOWER_LEFT, GOSY_CHEST_MIDDLE_PORTAL_ALCOVE, GOSY_CHEST_MIDDLE_PORTAL_POOL, GOSY_CHEST_UPPER_RIGHT_PORTAL_1, GOSY_CHEST_UPPER_RIGHT_PORTAL_2, GOSY_CHEST_UPPER_RIGHT_PORTAL_3, GOSY_CHEST_NEAR_GREENHOUSE_ROOF, GOSY_CHEST_GREENHOUSE, GOSY_CHEST_NEAR_UPPER_RIGHT_EXIT, GOSE_CHEST_LURKER_GUARDED, GOSE_CHEST_PHANTOM_GUARDED, GOSE_CHEST_SPIKE_HALL_UPPER_RIGHT, GOSE_CHEST_SPIKE_HALL_UPPER_LEFT, GOSE_CHEST_LURKERS_UNDER_WALKWAY, GOSE_CHEST_PORTAL_BELOW_NODE, GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_1, GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_2, GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_3, GOSE_CHEST_ABOVE_YINJIFU_TOMB, GOSE_YINJIFU_MURAL, GOSE_CARVINGS, GOSE_CHEST_OUTSIDE_GUIGUZI_TOMB, GOSE_GUIGUZI_MURAL, GOSW_CHEST_ABOVE_ELEVATOR, GOSW_CHEST_TOP_MIDDLE_ROOM, GOSW_CHEST_BELOW_WESTERN_CLIFFS, GOSW_CHEST_BELOW_LUYAN_TOMB, GOSW_LUYAN_MURAL, GOSW_YINJIFU_FLOWER, GOSW_GUIGUZI_FLOWER, GOSW_LUYAN_FLOWER, GOSW_CHEST_LEAR_GRAVE, ASP_JI, ASP_VITAL_SANCTUM, ASP_SHANHAI_CHIP, ST_CHEST_LOWER_ELEVATOR, ST_CHEST_WALL_CLIMB, ST_CHEST_HAZARDS, ST_CHEST_ROPES_BELOW_NODE, ST_FLOWER_STOWAWAY, ST_STOWAWAY, ST_CHEST_NODE, ST_CHEST_PINK_WATER, EDP_CHEST_ABOVE_TRANSPORTER, EDP_CHEST_STATUES, EDP_CHEST_NODE_ALCOVE_1, EDP_CHEST_NODE_ALCOVE_2, EDP_CHEST_SLIDER_RIGHT_OF_NODE, EDP_CHEST_LEFT_PINK_POOL, EDP_CHEST_FAR_LEFT, EDP_DROP_TIANSHOU, EDP_WATER_TOWER, EDP_CHEST_LASERS_1, EDP_CHEST_LASERS_2, EDP_CHEST_HALLWAY_TURRET, EDLA_CHEST_RIGHT_ELEVATOR, EDLA_SHANHAI_CHIP, EDLA_BULLETIN_BOARD, EDLA_CHEST_WALKING_EAST_BUILDING, EDLA_CHEST_ABOVE_NODE, EDLA_CHEST_MIDDLE_ELEVATOR, EDLA_DROP_MUTANT_MIDDLE_ELEVATOR, EDLA_VITAL_SANCTUM, EDLA_CHEST_FIVE_BELLS_UPPER_RIGHT, EDLA_DROP_MUTANT_FIVE_BELLS, EDLA_CHEST_THEATER_RIGHT, EDLA_CHEST_THEATER_LEFT, EDLA_CHEST_EAST_BUILDING_ROOF, EDLA_DGRD, EDLA_FLOWER, EDLA_CHEST_BACKER_1, EDLA_CHEST_BACKER_2, EDLA_CHEST_BACKER_3, EDS_MONITORING_PANEL, EDS_CHEST_EAST_ROOF_WALKING, EDS_CHEST_EAST_ROOF_RIGHT, EDS_CHEST_EAST_ROOF_ABOVE, EDS_DROP_MUTANT_BELOW_HALL, EDS_DROP_MUTANT_BELOW_NODE, EDS_ITEM_BOTTOM_LEFT, EDS_ITEM_ABOVE_BOTTOM_LEFT, NH_VITAL_SANCTUM, NH_CHEST_AFTER_FENGS, NH_NUWA_FLOWER, TRC_DROP_SHANHAI, TRC_CHEST_SPIKES, TRC_DROP_BOOKSHELF, TRC_CHEST_CHIEN_ARENA, TRC_CHEST_ABOVE_SOL_STATUE, TRC_ITEM_SICKBAY_VENTS, TRC_DGRD, TRC_DROP_MUTANT_HIGHEST, TRC_DROP_MUTANT_XINGTIAN, TRC_CHEST_MUTANT_BARRIER, TRC_DROP_MUTANT_SOL_STATUE, TRC_CHEST_DG_HQ_CHEST_NEAR_SCREEN, TRC_CHEST_DG_HQ_MUTANT_NEAR_SCREEN, TRC_DG_HQ_SCREEN } internal class LocationNames { public static Dictionary<Location, string> locationNames = new Dictionary<Location, string> { { Location.AFM_BREAK_CORPSE, "AF (Monitoring): Break Corpse" }, { Location.AFM_CHEST_UPPER_RIGHT, "AF (Monitoring): Upper Right" }, { Location.AFM_CHEST_LOWER_VENT, "AF (Monitoring): Lower Vent" }, { Location.AFM_DB_SURVEILLANCE, "AF (Monitoring): Examine Apeman Surveillance" }, { Location.AFE_CHEST_UPPER_PAGODA_LEFT, "AF (Elevator): Hidden Atop Upper Level Pagoda (Left Chest)" }, { Location.AFE_CHEST_UPPER_PAGODA_RIGHT, "AF (Elevator): Hidden Atop Upper Level Pagoda (Right Chest)" }, { Location.AFE_CHEST_MOVING_BOXES, "AF (Elevator): Moving Boxes" }, { Location.AFE_CHEST_ELEVATOR, "AF (Elevator): Elevator Shaft" }, { Location.AFE_DROP_BAICHANG, "AF (Elevator): Defeat Red Tiger Elite: Baichang" }, { Location.AFE_CHEST_STATUE, "AF (Elevator): Hack Statue" }, { Location.AFE_CHEST_OVER_HAZARD, "AF (Elevator): Over Electrified Floor" }, { Location.AFD_CHEST_CRYSTAL_CAVES, "AF (Depths): Crystal Caves" }, { Location.AFD_DROP_1_SHANGUI, "AF (Depths): Defeat Celestial Spectre: Shangui (1st Reward)" }, { Location.AFD_DROP_2_SHANGUI, "AF (Depths): Defeat Celestial Spectre: Shangui (2nd Reward)" }, { Location.AFD_CHEST_BELOW_NODE, "AF (Depths): Below Root Node" }, { Location.AFD_CHEST_STATUES, "AF (Depths): Hack 3 Statues" }, { Location.AFD_CHEST_LOWER_LEVEL, "AF (Depths): Lower Level" }, { Location.AFD_CHEST_UNDER_LOWER_LEFT_EXIT, "AF (Depths): Under Lower Left Exit" }, { Location.AFD_FLOWER_UNDER_ELEVATOR, "AF (Depths): Tianhuo Flower Under Elevator" }, { Location.AFD_CHEST_UPPER_RIGHT_1, "AF (Depths): Upper Right Chest (1st Reward)" }, { Location.AFD_CHEST_UPPER_RIGHT_2, "AF (Depths): Upper Right Chest (2nd Reward)" }, { Location.CH_COUNCIL_SIGN, "Central Hall: Examine Council Sign" }, { Location.CH_LAUNCH_MEMORIAL, "Central Hall: Examine Launch Memoral" }, { Location.CH_COUNCIL_TENETS, "Central Hall: Examine Council Tenets" }, { Location.CH_CHEST_AXEBOT_AND_TURRETS, "Central Hall: Turrets and Double Axe Robot Room" }, { Location.CH_CHEST_VENTS, "Central Hall: Vents" }, { Location.FSP_KUAFU_GIFT_1, "FSP: Kuafu's 1st Gift" }, { Location.FSP_KUAFU_GIFT_2, "FSP: Kuafu's 2nd Gift" }, { Location.FSP_SHUANSHUAN_MUSIC, "FSP: Decode the Ancient Sheet Music" }, { Location.FSP_SHUANSHUAN_PORTRAIT, "FSP: Have Yi's Portrait Painted" }, { Location.FSP_CHIYOU_PORTRAIT, "FSP: Give Yi's Portrait to Chiyou" }, { Location.FSP_SHENNONG_PBV_QUEST, "FSP: Receive Peach Blossom Village Quest" }, { Location.FSP_SHUANSHUAN_BOOK, "FSP: Take Shuanshuan's Book" }, { Location.FSP_CHIYOU_BOOK, "FSP: Give Shuanshuan's Book to Chiyou" }, { Location.FSP_SHUANSHUAN_HIDDEN_POEM, "FSP: Reveal the Kunlun Immortal Portrait's Secret" }, { Location.FSP_CHEST_HALF_TREE, "FSP: Half-Grown Tree Chest" }, { Location.FSP_CHEST_FULL_TREE_1, "FSP: Fully Grown Tree 1st Chest" }, { Location.FSP_CHEST_FULL_TREE_2, "FSP: Fully Grown Tree 2nd Chest" }, { Location.FSP_MUTANT_QUEST, "FSP: Isolate the Mutant Gene Sequence" }, { Location.FSP_KUAFU_HOMING_DARTS, "FSP: Give Kuafu the Homing Darts" }, { Location.FSP_KUAFU_THUNDERBURST_BOMB, "FSP: Give Kuafu the Thunderburst Bomb" }, { Location.CC_LADY_ETHEREAL, "Cortex Center: Defeat Lady Ethereal" }, { Location.CC_FLOWER_LADY_ETHEREAL, "Cortex Center: Tianhuo Flower After Soulscape" }, { Location.CC_SHANHAI_CHIP, "Cortex Center: Retrieve Chip From Shanhai 9000" }, { Location.CC_CHEST_CAVES_UPPER_RIGHT, "Cortex Center: Crystal Caves Upper Right" }, { Location.CC_CHEST_CAVES_CENTER, "Cortex Center: Crystal Caves Central Chamber" }, { Location.CC_CHEST_LEFT_EXIT, "Cortex Center: Near Left Exit" }, { Location.CTH_CHEST_LARGE_ELEVATOR, "CTH: Large Elevator Shaft" }, { Location.CTH_DROP_YANREN, "CTH: Defeat Red Tiger Elite: Yanren" }, { Location.CTH_CHEST_SIDE_ROOM, "CTH: Side Room Near Right Exit" }, { Location.CTH_CHEST_VENTS_LEFT, "CTH: Lower Left Vents" }, { Location.CTH_ANOMALOUS_ROOT_NODE, "CTH: Examine Panel by Root Node" }, { Location.CTH_CHEST_STATUES, "CTH: Hack 2 Statues" }, { Location.PRE_CHEST_AFTER_LASERS, "PR (East): After Lasers" }, { Location.PRE_CHEST_UNDER_BOX, "PR (East): Under Moving Box" }, { Location.PRE_CHEST_UPPER_RIGHT, "PR (East): Top Platform in Upper Right Shaft" }, { Location.PRE_CHEST_STATUE, "PR (East): Hack Statue" }, { Location.PRE_CHEST_UPPER_LEFT, "PR (East): Upper Left Room" }, { Location.PRE_DROP_JIAODUAN, "PR (East): Defeat Celestial Guardian: Jiaoduan" }, { Location.PRC_CHEST_BREAKABLE_WALL_RIGHT, "PR (Central): Breakable Wall Near Right Transporter" }, { Location.PRC_CHEST_NEAR_MOVING_BOX, "PR (Central): Near Moving Box" }, { Location.PRC_CHEST_STATUE, "PR (Central): Hack Statue" }, { Location.PRC_CHEST_GUARDED_BY_BEETLE, "PR (Central): Guarded By Beetle" }, { Location.PRC_CHEST_RIGHT_OF_PAGODA, "PR (Central): Near One-Way Door Right of Pagoda" }, { Location.PRC_SHANHAI_CHIP, "PR (Central): Retrieve Chip From Shanhai 9000" }, { Location.PRC_RHIZOMATIC_ENERGY_METER, "PR (Central): Examine Energy Meter" }, { Location.PRC_CHEST_LEFT_EXIT, "PR (Central): Near Left Transporter" }, { Location.PRC_CHEST_LEFT_OF_BRIDGE, "PR (Central): Left of Light Bridge" }, { Location.RP_CONTROL_PANEL, "Examine Radiant Pagoda Control Panel" }, { Location.RP_DROP_YINGZHAO, "Defeat General Yingzhao" }, { Location.RP_KUAFU_SANCTUM, "Kuafu's Vital Sanctum" }, { Location.PRW_CHEST_BELOW_NODE, "PR (West): Below Root Node" }, { Location.PRW_CHEST_GUARDED_BY_TURRET, "PR (West): Guarded By Turret" }, { Location.PRW_CHEST_VENTS, "PR (West): Vents" }, { Location.PRW_CHEST_STATUE, "PR (West): Hack Statue" }, { Location.PRW_DGRD, "PR (West): Dusk Guardian Recording Device" }, { Location.PRW_FLOWER, "PR (West): Tianhuo Flower" }, { Location.PRW_CHEST_RIGHT_EXIT, "PR (West): Near Right Transporter" }, { Location.LYR_CHEST_LEFT_POOL_MIDDLE_1, "LYR: First Chest in Leftmost Pool" }, { Location.LYR_CHEST_LEFT_POOL_MIDDLE_2, "LYR: Second Chest in Leftmost Pool" }, { Location.LYR_CHEST_LEFT_POOL_RIGHT, "LYR: Right of Leftmost Pool" }, { Location.LYR_CHEST_LEFT_POOL_ABOVE, "LYR: Above Leftmost Pool" }, { Location.LYR_JI_MUSIC, "LYR: Hear Ji Reminisce About Daybreak Tower" }, { Location.LYR_CHEST_TOWER, "LYR: Daybreak Tower Chest" }, { Location.LYR_CHEST_TOWER_ROOM, "LYR: Daybreak Tower Bell Puzzle" }, { Location.LYR_CHEST_ABOVE_NODE, "LYR: Above Root Node" }, { Location.LYR_CHEST_STATUES_ROOM, "LYR: Statue Hack Room Near Root Node" }, { Location.LYR_LAKE_STELE, "LYR: Examine Stele" }, { Location.LYR_CHEST_NYMPH_ROOM, "LYR: Nymph Puzzle Room Near Stele" }, { Location.LYR_CHEST_RIGHT_EXIT, "LYR: Ropes Near Right Exit" }, { Location.GH_CHEST_NYMPH_ROPE, "Greenhouse: Hackable Rope Near Wreckage" }, { Location.GH_WATER_REPORT, "Greenhouse: Examine Water Report" }, { Location.GH_SHANHAI_CHIP, "Greenhouse: Retrieve Chip From Shanhai 9000" }, { Location.GH_CHEST_RIGHT_HANGING_POOL, "Greenhouse: Near Rightmost Hanging Pool" }, { Location.GH_UPPER_LEVEL_FOLIAGE, "Greenhouse: Hidden in Upper Level Foliage" }, { Location.GH_UNDERWATER_VASE, "Greenhouse: Vase In Water Above Root Node" }, { Location.GH_CHEST_LEFT_HANGING_POOL, "Greenhouse: In Leftmost Hanging Pool" }, { Location.GH_MUTATED_CROPS, "Greenhouse: Examine Mutated Crops" }, { Location.GH_DROP_SHUIGUI, "Greenhouse: Defeat Celestial Spectre: Shuigui" }, { Location.WOS_CHEST_HIGH_PLATFORMS_RIGHT, "W&OS: High Platforms Right of Center" }, { Location.WOS_CHEST_HIGH_PLATFORMS_LEFT, "W&OS: High Platforms Left of Center" }, { Location.WOS_FLOWER, "W&OS: Tianhuo Flower" }, { Location.WOS_DGRD, "W&OS: Dusk Guardian Recording Device" }, { Location.WOS_PIPELINE_PANEL, "W&OS: Examine Pipeline Panel" }, { Location.WOS_SHAFT_NEAR_NODE, "W&OS: Climb Elevator Shaft By Root Node" }, { Location.AH_CHEST_GOUMANG_1, "Chest After Goumang (1st Reward)" }, { Location.AH_CHEST_GOUMANG_2, "Chest After Goumang (2nd Reward)" }, { Location.AH_GOUMANG_SANCTUM, "Goumang's Vital Sanctum" }, { Location.YC_CHEST_UPPER_CAVES_TOP_LEFT, "Yinglong Canal: Top Left of Upper Caves" }, { Location.YC_CHEST_UPPER_CAVES_BOTTOM_LEFT, "Yinglong Canal: Bottom Left of Upper Caves" }, { Location.YC_FARMLAND_MARKINGS, "Yinglong Canal: Examine Farmland Markings" }, { Location.YC_ABOVE_MARKINGS, "Yinglong Canal: Climbing Puzzle Above Farmland Markings" }, { Location.YC_CHEST_MIDDLE_CAVE, "Yinglong Canal: Between Egg Cave and Farmland Markings" }, { Location.YC_CAVE_EGG, "Yinglong Canal: Break Center Yinglong Egg" }, { Location.YC_NEAR_NODE, "Yinglong Canal: Near Root Node" }, { Location.FGH_CHEST_RIGHT_SHIELD_ORB, "Factory (GH): Near Rightmost Shield Orb" }, { Location.FGH_CHEST_NEAR_NODE, "Factory (GH): Near Root Node" }, { Location.FGH_CHEST_MIDDLE_SHIELD_ORB, "Factory (GH): Near Middle Shield Orb" }, { Location.FGH_HAMMER_FLOWER, "Factory (GH): Tianhuo Flower Below Hammers" }, { Location.FGH_HAMMER_BROS, "Factory (GH): Break the Hammers" }, { Location.FGH_MONITOR, "Factory (GH): Examine Furnance Monitor" }, { Location.FGH_CHIYOU_BRIDGE, "Factory (GH): Raise the Bridge for Chiyou" }, { Location.FGH_CHEST_ABOVE_NODE, "Factory (GH): Roof Above Root Node" }, { Location.FGH_CHEST_RIGHT_ELEVATOR, "Factory (GH): Near Right Elevator" }, { Location.FGH_PLATFORM_ROOM_BALL, "Factory (GH): Ball Drop in Platform Puzzle Room" }, { Location.FGH_PLATFORM_ROOM_DGRD, "Factory (GH): Recording Device in Platform Puzzle Room" }, { Location.FGH_PLATFORM_ROOM_FLOWER, "Factory (GH): Tianhuo Flower in Platform Puzzle Room" }, { Location.FU_CHEST_UPPER_RIGHT_EXIT, "Factory (U): Near Upper Right Exit" }, { Location.FU_DROP_KUIYAN, "Factory (U): Defeat Red Tiger Elite: Kuiyan" }, { Location.FU_CHEST_LOWER_ELEVATOR, "Factory (U): Near Lower Elevator" }, { Location.FU_CHEST_STATUES, "Factory (U): Hack 2 Statues" }, { Location.FU_DROP_SHANHAI, "Factory (U): Find Broken Shanhai 9000" }, { Location.FU_EVACUATION_NOTICE, "Factory (U): Examine Evacuation Notice" }, { Location.FU_CHEST_ABOVE_NODE, "Factory (U): Above Root Node" }, { Location.FU_CHEST_BELOW_LEFT_EXIT, "Factory (U): Below Walkway to Left Exit" }, { Location.FU_CHEST_BEHIND_BOXES, "Factory (U): Behind Moving Boxes" }, { Location.P_SCROLL_LOWER_RIGHT, "Prison: Examine Scroll in Lower Right Cell" }, { Location.P_CHEST_LOWER_LEFT, "Prison: Lower Left Cell" }, { Location.P_CHEST_ABOVE_STAIRS, "Prison: Above Stairs on Second Level" }, { Location.P_CHEST_NEAR_NODE, "Prison: Near Root Node" }, { Location.P_SCROLL_UPPER_LEFT, "Prison: Examine Scroll in Upper Left Cell" }, { Location.P_CHESTS_BEFORE_KANGHUI_1, "Prison: 1st Chest in Room Before Kanghui" }, { Location.P_CHESTS_BEFORE_KANGHUI_2, "Prison: 2nd Chest in Room Before Kanghui" }, { Location.P_CHESTS_BEFORE_KANGHUI_3, "Prison: 3rd Chest in Room Before Kanghui" }, { Location.P_DROP_KANGHUI, "Prison: Defeat Kanghui" }, { Location.FMR_CHEST_RIGHT_ELEVATOR, "Factory (MR): Below Right Elevator" }, { Location.FMR_CHEST_ELEVATOR_TURRET, "Factory (MR): Break Turret Below Elevator" }, { Location.FMR_CHEST_LEFT_ELEVATOR, "Factory (MR): Above Left Elevator" }, { Location.FMR_CHEST_WALKING_GREEN_PILLAR, "Factory (MR): Walking Chest Above Green Pillar" }, { Location.FMR_CHEST_TURRET_GREEN_PILLAR, "Factory (MR): Behind Turret Near Green Pillar" }, { Location.FMR_CHEST_NEAR_MOVING_PLATFORMS, "Factory (MR): Near Moving Platforms" }, { Location.FMR_FLOWER, "Factory (MR): Tianhuo Flower Above Right Elevator" }, { Location.FPA_CHEST_VENTS_LOWER_LEFT, "Factory (PA): Behind Fire in Lower Left Vents" }, { Location.FPA_CHEST_NEAR_JIEQUAN_STATUE, "Factory (PA): Hallway Near Jiequan Statue" }, { Location.FPA_CHEST_TRIPLE_GUARD_SPAWNER, "Factory (PA): Three Guard Spawners" }, { Location.FPA_CHEST_VENTS_UPPER_LEFT, "Factory (PA): Behind Hack in Upper Left Vents" }, { Location.FPA_PRODUCTION_STATION, "Factory (PA): Examine Production Station" }, { Location.FPA_VENTS_BELOW_PRODUCTION, "Factory (PA): Vents Below Production Station" }, { Location.FPA_CHEST_BELOW_DOUBLE_SNIPER, "Factory (PA): Below Double Sniper Zone" }, { Location.FPA_PHARMACY_PANEL, "Factory (PA): Examine Pharmacy Panel" }, { Location.FPA_PHARMACY_BALL, "Factory (PA): Ball Drop in Pharmacy" }, { Location.FPA_CHEST_RIGHT_FIRE_ZONE_BOTTOM, "Factory (PA): Bottom of Right Fire Zone" }, { Location.FPA_DEFEAT_SHANHAI, "Factory (PA): Defeat Shanhai 9000" }, { Location.FPA_RIGHT_FIRE_ZONE_TOP, "Factory (PA): Top of Right Fire Zone" }, { Location.FPA_CHEST_PAST_WUQIANG, "Factory (PA): Far Side of Wuqiang Arena" }, { Location.FPA_DROP_WUQIANG, "Factory (PA): Defeat Celestial Sentinel: Wuqiang" }, { Location.SH_HAOTIAN_SPHERE, "Examine Sphere Before Jiequan" }, { Location.SH_CHEST_RIGHT, "Chest Before Jiequan" }, { Location.SH_JIEQUAN_FLOWER, "Jiequan's Tianhuo Flower" }, { Location.SH_CHEST_LEFT, "Chest After Jiequan" }, { Location.SH_JIEQUAN_SANCTUM, "Jiequan's Vital Sanctum" }, { Location.AM_CHEST_ABOVE_LEFT_EXIT, "AM: Above Left Exit" }, { Location.AM_DROP_YINYUE, "AM: Defeat Celestial Warden: Yinyue" }, { Location.AM_CHEST_NEAR_NODE, "AM: Near Root Node" }, { Location.AM_CHEST_WALKING, "AM: Walking Chest" }, { Location.AM_FLOWER, "AM: Tianhuo Flower" }, { Location.AM_CHEST_AFTER_FLOWER_1, "AM: 1st Chest After Flower" }, { Location.AM_CHEST_AFTER_FLOWER_2, "AM: 2nd Chest After Flower" }, { Location.UC_INSCRIPTION, "UC: Examine Stone Inscription" }, { Location.UC_NOTE, "UC: Examine Note" }, { Location.UC_SCROLL, "UC: Examine Scroll" }, { Location.LHP_DROP_LIEGUAN, "Village: Defeat Red Tiger Elite: Lieguan" }, { Location.GD_SIGN, "Galactic Dock: Examine Sign" }, { Location.GD_FLOWER, "Galactic Dock: Tianhuo Flower" }, { Location.GD_SHAMAN, "Galactic Dock: Shaman's Gift" }, { Location.OW_SHANHAI_CHIP, "OW: Retrieve Chip From Shanhai 9000" }, { Location.OW_CHEST_ABOVE_SOL_STATUE, "OW: Above Sol Statue" }, { Location.OW_CHEST_CRUSHER_GAUNTLET, "OW: Crusher Gauntlet" }, { Location.OW_CHEST_GAUNTLET_ROOM, "OW: Enemy Gauntlet Room" }, { Location.OW_CHEST_VENTS_ROBOT, "OW: Vents Above Robot" }, { Location.OW_DROP_VENT_CRATE, "OW: Inside Crate Dropped From Vent Hack" }, { Location.OW_DATABASE, "OW: Examine Warehouse Database" }, { Location.IW_CHEST_STATUES, "IW: Hack 3 Statues" }, { Location.IW_CHEST_WALKING, "IW: Shielded Walking Chest" }, { Location.IW_DROP_TIEYAN, "IW: Defeat Celestial Enforcer: Tieyan" }, { Location.IW_NYMPH_PUZZLE_ROOM, "IW: Nymph Puzzle Room" }, { Location.IW_DGRD, "IW: Dusk Guardian Recording Device" }, { Location.IW_FLOWER, "IW: Tianhuo Flower" }, { Location.BR_CONSOLE, "BR: Examine Console" }, { Location.BR_CHEST_NEAR_CONSOLE, "BR: Near Xingtian Console" }, { Location.BR_GAUNTLET_1_CHEST, "BR: Gauntlet Part 1 Chest" }, { Location.BR_GAUNTLET_2_CHEST, "BR: Gauntlet Part 2 First Chest" }, { Location.BR_GAUNTLET_2_CHEST_LASERS, "BR: Gauntlet Part 2 Chest Past Lasers" }, { Location.BR_GAUNTLET_2_CHEST_BEETLE, "BR: Gauntlet Part 2 Chest Past Beetle" }, { Location.BR_VAULT_CHEST_1, "BR: Vault 1st Chest" }, { Location.BR_VAULT_CHEST_2, "BR: Vault 2nd Chest" }, { Location.BR_VAULT_SCROLL, "BR: Examine Vault Scroll" }, { Location.BR_VAULT_CHEST_3, "BR: Vault 3rd Chest" }, { Location.BR_VAULT_CHEST_4, "BR: Vault 4th Chest" }, { Location.YH_FLOWER, "Yanlao's Tianhuo Flower" }, { Location.YH_VITAL_SANCTUM, "Yanlao's Vital Sanctum" }, { Location.GOSY_PAINTING, "GoS (Entry): Examine Painting" }, { Location.GOSY_COFFIN, "GoS (Entry): Examine Coffin" }, { Location.GOSY_CHEST_TREASURE_1, "GoS (Entry): Poem Treasure 1st Chest" }, { Location.GOSY_CHEST_TREASURE_2, "GoS (Entry): Poem Treasure 2nd Chest" }, { Location.GOSY_CHEST_TREASURE_3, "GoS (Entry): Poem Treasure 3rd Chest" }, { Location.GOSY_CHEST_TREASURE_4, "GoS (Entry): Poem Treasure 4th Chest" }, { Location.GOSY_CHEST_TREASURE_5, "GoS (Entry): Poem Treasure 5th Chest" }, { Location.GOSY_CHEST_TREASURE_6, "GoS (Entry): Poem Treasure 6th Chest" }, { Location.GOSY_CHEST_LOWER_PORTAL, "GoS (Entry): Lower Caves Portal" }, { Location.GOSY_CHEST_CAVES_LOWER_LEFT, "GoS (Entry): Lower Left Caves" }, { Location.GOSY_CHEST_MIDDLE_PORTAL_ALCOVE, "GoS (Entry): Alcove Above Middle Caves Portal" }, { Location.GOSY_CHEST_MIDDLE_PORTAL_POOL, "GoS (Entry): Yellow Pool Above Middle Caves Portal" }, { Location.GOSY_CHEST_UPPER_RIGHT_PORTAL_1, "GoS (Entry): Upper Right Caves Portal 1st Chest" }, { Location.GOSY_CHEST_UPPER_RIGHT_PORTAL_2, "GoS (Entry): Upper Right Caves Portal 2nd Chest" }, { Location.GOSY_CHEST_UPPER_RIGHT_PORTAL_3, "GoS (Entry): Upper Right Caves Portal 3rd Chest" }, { Location.GOSY_CHEST_NEAR_GREENHOUSE_ROOF, "GoS (Entry): Near Greenhouse Roof" }, { Location.GOSY_CHEST_GREENHOUSE, "GoS (Entry): Greenhouse Between Elevators" }, { Location.GOSY_CHEST_NEAR_UPPER_RIGHT_EXIT, "GoS (Entry): Near Upper Right Exit" }, { Location.GOSE_CHEST_LURKER_GUARDED, "GoS (East): Lurker Near Lower Exit" }, { Location.GOSE_CHEST_PHANTOM_GUARDED, "GoS (East): Guarded By Phantom Ninja" }, { Location.GOSE_CHEST_SPIKE_HALL_UPPER_RIGHT, "GoS (East): Spike Ball Hall Upper Right" }, { Location.GOSE_CHEST_SPIKE_HALL_UPPER_LEFT, "GoS (East): Spike Ball Hall Upper Left" }, { Location.GOSE_CHEST_LURKERS_UNDER_WALKWAY, "GoS (East): Lurkers Under Tunnel Walkway" }, { Location.GOSE_CHEST_PORTAL_BELOW_NODE, "GoS (East): Portal Below Root Node" }, { Location.GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_1, "GoS (East): Alcove Between Tombs 1st Chest" }, { Location.GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_2, "GoS (East): Alcove Between Tombs 2nd Chest" }, { Location.GOSE_CHEST_ALCOVE_BETWEEN_TOMBS_3, "GoS (East): Alcove Between Tombs 3rd Chest" }, { Location.GOSE_CHEST_ABOVE_YINJIFU_TOMB, "GoS (East): Upper Right of Room Above Yin Jifu's Tomb" }, { Location.GOSE_YINJIFU_MURAL, "GoS (East): Examine Mural in Yin Jifu's Tomb" }, { Location.GOSE_CARVINGS, "GoS (East): Examine Stone Carvings" }, { Location.GOSE_CHEST_OUTSIDE_GUIGUZI_TOMB, "GoS (East): Outside Guiguzi's Tomb" }, { Location.GOSE_GUIGUZI_MURAL, "GoS (East): Examine Mural in Guiguzi's Tomb" }, { Location.GOSW_CHEST_ABOVE_ELEVATOR, "GoS (West): Above Elevator" }, { Location.GOSW_CHEST_TOP_MIDDLE_ROOM, "GoS (West): Platforms In Top Middle Room" }, { Location.GOSW_CHEST_BELOW_WESTERN_CLIFFS, "GoS (West): Below Western Cliffs" }, { Location.GOSW_CHEST_BELOW_LUYAN_TOMB, "GoS (West): Below Luyan's Tomb" }, { Location.GOSW_LUYAN_MURAL, "GoS (West): Examine Mural in Luyan's Tomb" }, { Location.GOSW_YINJIFU_FLOWER, "GoS (West): Yin Jifu's Tianhuo Flower" }, { Location.GOSW_GUIGUZI_FLOWER, "GoS (West): Guiguzi's Tianhuo Flower" }, { Location.GOSW_LUYAN_FLOWER, "GoS (West): Luyan's Tianhuo Flower" }, { Location.GOSW_CHEST_LEAR_GRAVE, "GoS (West): Chest in Lear's Grave" }, { Location.ASP_JI, "Examine Ji" }, { Location.ASP_VITAL_SANCTUM, "Ji's Vital Sanctum" }, { Location.ASP_SHANHAI_CHIP, "Retrieve Chip From Shanhai 1000" }, { Location.ST_CHEST_LOWER_ELEVATOR, "Sky Tower: Above Lower Elevator" }, { Location.ST_CHEST_WALL_CLIMB, "Sky Tower: Wall Climb Platform" }, { Location.ST_CHEST_HAZARDS, "Sky Tower: Surrounded By Hazards" }, { Location.ST_CHEST_ROPES_BELOW_NODE, "Sky Tower: Ropes Below Root Node" }, { Location.ST_FLOWER_STOWAWAY, "Sky Tower: Stowaway's Tianhuo Flower" }, { Location.ST_STOWAWAY, "Sky Tower: Examine Stowaway's Belongings" }, { Location.ST_CHEST_NODE, "Sky Tower: By Root Node" }, { Location.ST_CHEST_PINK_WATER, "Sky Tower: Pink Water Near Root Node" }, { Location.EDP_CHEST_ABOVE_TRANSPORTER, "ED (Passages): Above Transporter" }, { Location.EDP_CHEST_STATUES, "ED (Passages): Hack 2 Statues" }, { Location.EDP_CHEST_NODE_ALCOVE_1, "ED (Passages): Alcove Right of Root Node 1st Chest" }, { Location.EDP_CHEST_NODE_ALCOVE_2, "ED (Passages): Alcove Right of Root Node 2nd Chest" }, { Location.EDP_CHEST_SLIDER_RIGHT_OF_NODE, "ED (Passages): Slider Right Of Root Node" }, { Location.EDP_CHEST_LEFT_PINK_POOL, "ED (Passages): Above Pink Pool Left Of Root Node" }, { Location.EDP_CHEST_FAR_LEFT, "ED (Passages): Far Left Rooms" }, { Location.EDP_DROP_TIANSHOU, "ED (Passages): Defeat The Great Miner: Tianshou" }, { Location.EDP_WATER_TOWER, "ED (Passages): Examine Water Tower" }, { Location.EDP_CHEST_LASERS_1, "ED (Passages): Alcove Between Lasers" }, { Location.EDP_CHEST_LASERS_2, "ED (Passages): Covered By Lasers" }, { Location.EDP_CHEST_HALLWAY_TURRET, "ED (Passages): Above Upper Hallway Turret" }, { Location.EDLA_CHEST_RIGHT_ELEVATOR, "ED (Living Area): Above Right Elevator" }, { Location.EDLA_SHANHAI_CHIP, "ED (Living Area): Retrieve Chip From Shanhai 9000" }, { Location.EDLA_BULLETIN_BOARD, "ED (Living Area): Examine Bulletin Board" }, { Location.EDLA_CHEST_WALKING_EAST_BUILDING, "ED (Living Area): Walking Chest In East Building" }, { Location.EDLA_CHEST_ABOVE_NODE, "ED (Living Area): Above Root Node" }, { Location.EDLA_CHEST_MIDDLE_ELEVATOR, "ED (Living Area): Middle Elevator Chest Room" }, { Location.EDLA_DROP_MUTANT_MIDDLE_ELEVATOR, "ED (Living Area): Defeat Mutated Zouyan in Middle Elevator Room" }, { Location.EDLA_VITAL_SANCTUM, "ED (Living Area): Fuxi's Vital Sanctum" }, { Location.EDLA_CHEST_FIVE_BELLS_UPPER_RIGHT, "ED (Living Area): Upper Right of Five Bells Room" }, { Location.EDLA_DROP_MUTANT_FIVE_BELLS, "ED (Living Area): Defeat Mutated A-Shou in Five Bells Room" }, { Location.EDLA_CHEST_THEATER_RIGHT, "ED (Living Area): Roof Right of Opera Theater" }, { Location.EDLA_CHEST_THEATER_LEFT, "ED (Living Area): Roof Left of Opera Theater" }, { Location.EDLA_CHEST_EAST_BUILDING_ROOF, "ED (Living Area): East Building Rooftops" }, { Location.EDLA_DGRD, "ED (Living Area): Dusk Guardian Recording Device" }, { Location.EDLA_FLOWER, "ED (Living Area): Tianhuo Flower" }, { Location.EDLA_CHEST_BACKER_1, "ED (Living Area): Backer Room 1st Chest" }, { Location.EDLA_CHEST_BACKER_2, "ED (Living Area): Backer Room 2nd Chest" }, { Location.EDLA_CHEST_BACKER_3, "ED (Living Area): Backer Room 3rd Chest" }, { Location.EDS_MONITORING_PANEL, "ED (Sanctum): Examine Monitoring Panel" }, { Location.EDS_CHEST_EAST_ROOF_WALKING, "ED (Sanctum): East Rooftop Walking Chest" }, { Location.EDS_CHEST_EAST_ROOF_RIGHT, "ED (Sanctum): Right Of East Rooftop" }, { Location.EDS_CHEST_EAST_ROOF_ABOVE, "ED (Sanctum): Above East Rooftop" }, { Location.EDS_DROP_MUTANT_BELOW_HALL, "ED (Sanctum): Defeat Mutated Xipu Below Nobility Hall" }, { Location.EDS_DROP_MUTANT_BELOW_NODE, "ED (Sanctum): Defeat Mutated Yangfan Below Root Node" }, { Location.EDS_ITEM_BOTTOM_LEFT, "ED (Sanctum): Lower Left Garden" }, { Location.EDS_ITEM_ABOVE_BOTTOM_LEFT, "ED (Sanctum): Above Lower Left Garden" }, { Location.NH_VITAL_SANCTUM, "Nuwa's Vital Sanctum" }, { Location.NH_CHEST_AFTER_FENGS, "Chest After Fengs" }, { Location.NH_NUWA_FLOWER, "Nuwa's Tianhuo Flower" }, { Location.TRC_DROP_SHANHAI, "TRC: Find Broken Shanhai 9000" }, { Location.TRC_CHEST_SPIKES, "TRC: Covered In Spikes" }, { Location.TRC_DROP_BOOKSHELF, "TRC: Ground Floor Bookshelf" }, { Location.TRC_CHEST_CHIEN_ARENA, "TRC: Before Chien Arena" }, { Location.TRC_CHEST_ABOVE_SOL_STATUE, "TRC: Above Sol Statue" }, { Location.TRC_ITEM_SICKBAY_VENTS, "TRC: Past Spikes In Sickbay Vents" }, { Location.TRC_DGRD, "TRC: Dusk Guardian Recording Device" }, { Location.TRC_DROP_MUTANT_HIGHEST, "TRC: Destroy Tendril In The Highest Room" }, { Location.TRC_DROP_MUTANT_XINGTIAN, "TRC: Defeat Mutated Shiyangyue Near Xingtian Sickbay" }, { Location.TRC_CHEST_MUTANT_BARRIER, "TRC: Behind Mutant Barrier" }, { Location.TRC_DROP_MUTANT_SOL_STATUE, "TRC: Destroy Tendril Near Sol Statue" }, { Location.TRC_CHEST_DG_HQ_CHEST_NEAR_SCREEN, "TRC: Chest Near Dusk Guardian HQ Screen" }, { Location.TRC_CHEST_DG_HQ_MUTANT_NEAR_