Decompiled source of ArchipelagoRandomizer v0.1.4
Archipelago.MultiClient.Net.dll
Decompiled a month 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 a month 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 Com.LuisPedroFonseca.ProCamera2D; using Cysharp.Threading.Tasks; using HarmonyLib; using I2.Loc; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NineSolsAPI; using RCGFSM.Animation; 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+1f6a4bb0bf25d36cc48454c72b69b099b021dcac")] [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; public ConfigEntry<bool> ShowAPMessagesSetting; private Harmony harmony; public static APRandomizer Instance; public static string SaveSlotsPath => Application.persistentDataPath; private void Awake() { //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) Log.Init(((BaseUnityPlugin)this).Logger); RCGLifeCycle.DontDestroyForever(((Component)this).gameObject); Instance = this; 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); }; ShowAPMessagesSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("", "Show Archipelago Messages In-Game", true, "Display all messages the Archipelago server sends to clients in the main game window. Turn this off if you find the AP messages too spammy.\n\nThe messages can still be read in the BepInEx console window, or in any AP text client you have connected to the same slot."); 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() { Jiequan1Fight.Update(); LadyESoulscapeEntrance.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_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Expected O, but got Unknown //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Expected O, but got Unknown //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Expected O, but got Unknown //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05c6: 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.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Enable Weakened Prison State", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"enabling weakened prison state"); PlayerAbilityScenarioModifyPack val = (PlayerAbilityScenarioModifyPack)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["df6a9a9f7748f4baba6207afdf10ea31PlayerAbilityScenarioModifyPack"]; val.ApplyOverriding((IStatModifierOwner)val); } if (GUILayout.Button("Disable Weakened Prison State", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"disabling weakened prison state"); PlayerAbilityScenarioModifyPack val2 = (PlayerAbilityScenarioModifyPack)SingletonBehaviour<SaveManager>.Instance.allFlags.FlagDict["df6a9a9f7748f4baba6207afdf10ea31PlayerAbilityScenarioModifyPack"]; val2.RevertApply((IStatModifierOwner)val2); } if (GUILayout.Button("Toggle Hat", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"toggling hat"); Player i = Player.i; if (Object.op_Implicit((Object)(object)i)) { bool flag = AccessTools.FieldRefAccess<Player, bool>("_hasHat").Invoke(i); i.SetHasHat(!flag); } } if (GUILayout.Button("Toggle Story Walk", buttonStyle, Array.Empty<GUILayoutOption>())) { ToastManager.Toast((object)"toggling story walk"); Player i2 = Player.i; if (i2 != null) { i2.SetStoryWalk(!i2.IsStoryWalk, 1f); } } GUILayout.EndHorizontal(); GUILayout.Label("", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Core Progression Items", centeredLabelStyle, Array.Empty<GUILayoutOption>()); GUIStyle val3 = new GUIStyle(labelStyle); val3.fixedWidth = 200f; GUIStyle val4 = new GUIStyle(buttonStyle); val4.fixedWidth = 50f; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("MysticNymphScoutMode", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.MysticNymphScoutMode, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.MysticNymphScoutMode, 0); } GUILayout.Label("TaiChiKick", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.TaiChiKick, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.TaiChiKick, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("ChargedStrike", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.ChargedStrike, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.ChargedStrike, 0); } GUILayout.Label("AirDash", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.AirDash, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.AirDash, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("UnboundedCounter", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.UnboundedCounter, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.UnboundedCounter, 0); } GUILayout.Label("CloudLeap", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.CloudLeap, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.CloudLeap, 0); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("SuperMutantBuster", val3, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("On", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.SuperMutantBuster, 1); } if (GUILayout.Button("Off", val4, Array.Empty<GUILayoutOption>())) { ItemApplications.UpdateItemCount(Item.SuperMutantBuster, 0); } GUILayout.EndHorizontal(); GUIStyle val5 = new GUIStyle(buttonStyle); val5.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", val5, 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) { return; } bool flag = false; if (text == "85d361b9-7c43-4a1d-91c2-fd19e4bbb0b1_6b037c39982d34953a066792ab66783aScriptableDataBool") { flag = true; } else if (text == "e965aab1c014b4273b928b17fbcff379ScriptableDataBool") { flag = true; } if (flag) { if (PlayerGamePlayData.Instance.memoryMode.CurrentValue) { Log.Info("Goal code ignoring Eigong kill because this is Battle Memories mode"); return; } 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) { try { foreach (var (value, value2) in apSaveData.itemsAcquired) { ApInventory[Enum.Parse<Item>(value)] = value2; } } catch (Exception ex) { Log.Error("Caught error in LoadSavedInventory: '" + ex.Message + "'\n" + ex.StackTrace); } } public static void SyncInventoryWithServer() { try { 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); } } catch (Exception ex) { Log.Error("Caught error in SyncInventoryWithServer: '" + ex.Message + "'\n" + ex.StackTrace); } } 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 text = item.ToString(); Log.Info($"SyncItemCountWithAPServer mapped id {itemId} to item {item} / {text}"); if (APSaveManager.CurrentAPSaveData == null) { Log.Error("Somehow CurrentAPSaveData is null during a SyncItemCountWithAPServer() call. We should've created or loaded save data when connecting to the AP server."); return; } if (APSaveManager.CurrentAPSaveData.itemsAcquired == null) { Log.Error("Somehow CurrentAPSaveData.itemsAcquired is null during a SyncItemCountWithAPServer() call."); return; } Dictionary<string, int> itemsAcquired = APSaveManager.CurrentAPSaveData.itemsAcquired; if (!itemsAcquired.ContainsKey(text)) { itemsAcquired[text] = 0; } if (ConnectionAndPopups.APSession == null) { Log.Error("Somehow APSession is null during a SyncItemCountWithAPServer() call. How did this get called without an AP connection?"); return; } int num = ConnectionAndPopups.APSession.Items.AllItemsReceived.Where((ItemInfo i) => i.ItemId == itemId).Count(); int num2 = itemsAcquired[text]; if (num2 >= num) { if (num2 == num) { Log.Info($"SyncItemCountWithAPServer ignoring item {item} since savedCount ({num2}) == itemCountSoFar ({num})."); } else { Log.Info($"SyncItemCountWithAPServer ignoring item {item} since savedCount ({num2}) > itemCountSoFar ({num}), which usually means the client-side cache hasn't finished updating yet."); } } else { 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 Furnace 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"