Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PlayerTradingReforged v1.0.1
plugins/PlayerTradingReforged.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using PlayerTradingReforged.GUI; using PlayerTradingReforged.Patterns; using PlayerTradingReforged.Trading; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Player Trading Reforged")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Player Trading Reforged")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("F9B815D6-6EF4-44DB-9A0F-436849B24670")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PlayerTradingReforged { [Serializable] public class StringLocalization { public string TradePending = "Trade confirmation pending. Items are held safely until the other player reconnects."; public string TradeInvalid = "Trade cancelled: the offered items could not be read. Both players need the same mod and item versions."; public string ReturnedItems = "Trade items returned. Make inventory space to recover any remaining items."; public string RequestTrade = "Request Trade"; public string AcceptRequest = "Accept Trade"; public string TradeRequestSent = "Trade request sent"; public string TradeRecentlySent = "Trade request recently sent"; public string StartedTradeWithX = "Started trading with"; public string XWantsToTrade = "wants to trade"; public string XHasCancelledTrade = "has cancelled the trade"; public string LocalPlayerCancelledTrade = "You have cancelled the trade"; public string CantStartNewTradeInstance = "You cannot start a new trade session"; public string TradeSuccessful = "Trade successful"; public string NotEnoughInventorySlots = "Not enough inventory slots"; public string ToGiveWindowText = "You Will Give"; public string ToReceiveWindowText = "You Will Receive"; public string PartnerInventoryText = "Partner's Inventory"; public string ReservedForTradeText = "{0} reserved for trade"; public string YourInventoryText = "Your Inventory"; public string NamedInventoryText = "{0}’s Inventory"; public string ProjectedWeightText = "After trade"; public string PartnerInventoryWaiting = "Waiting for Partner's Inventory"; public string PartnerInventoryUnavailable = "Partner's Inventory Unavailable"; public string AcceptTradeButtonText = "Accept Trade"; public string ChangeTradeButtonText = "Change Trade"; public string CancelTradeButtonText = "Cancel Trade"; public string EditModeOn = "Edit UI Mode ON"; public string EditModeOff = "Edit UI Mode OFF"; } [BepInPlugin("augusdogus.mods.PlayerTradingReforged", "Player Trading Reforged", "1.0.1")] public sealed class Plugin : BaseUnityPlugin { public const string PluginId = "augusdogus.mods.PlayerTradingReforged"; public const string PluginVersion = "1.0.1"; private static Plugin? _instance; private readonly Harmony _harmony = new Harmony("augusdogus.mods.PlayerTradingReforged"); private TradeHandler? _handler; private ZRoutedRpc? _network; public static StringLocalization Localization { get; private set; } = new StringLocalization(); private static ConfigFile Settings => ((BaseUnityPlugin)(_instance ?? throw new InvalidOperationException("Player Trading Reforged has not initialized."))).Config; public static ConfigEntry<bool> UseModifierKey => Settings.Bind<bool>("Keybinds", "useModifierKey", false, "Require a modifier when requesting or accepting a trade."); public static ConfigEntry<KeyCode> ModifierKey => Settings.Bind<KeyCode>("Keybinds", "modifierKey", (KeyCode)308, "Trade modifier key."); public static ConfigEntry<KeyCode> EditWindowLayoutKey => Settings.Bind<KeyCode>("Keybinds", "editWindowLayoutKey", (KeyCode)292, "Toggle window placement mode."); public static ConfigEntry<Vector2> ToGiveUserOffset => Offset("toGiveUserOffset"); public static ConfigEntry<Vector2> ToReceiveUserOffset => Offset("toReceiveUserOffset"); public static ConfigEntry<Vector2> PartnerInventoryUserOffset => Offset("partnerInventoryUserOffset"); public static ConfigEntry<Vector2> AcceptButtonUserOffset => Offset("acceptButtonUserOffset"); public static ConfigEntry<Vector2> CancelButtonUserOffset => Offset("cancelButtonUserOffset"); internal static event Action? OnLocalPlayerChanged; private static ConfigEntry<Vector2> Offset(string key) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return Settings.Bind<Vector2>("Offsets", key, Vector2.zero, "Trade UI position offset. Set to 0, 0 to reset."); } private void Awake() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)SystemInfo.graphicsDeviceType == 4) { ((Behaviour)this).enabled = false; return; } _instance = this; _ = UseModifierKey; _ = ModifierKey; _ = EditWindowLayoutKey; _ = ToGiveUserOffset; _ = ToReceiveUserOffset; _ = AcceptButtonUserOffset; _ = CancelButtonUserOffset; _ = PartnerInventoryUserOffset; InitLocalization(); _harmony.PatchAll(typeof(Plugin).Assembly); } private void InitLocalization() { string text = Path.Combine(Paths.ConfigPath, "augusdogus.mods.PlayerTradingReforged.strings.json"); try { Directory.CreateDirectory(Paths.ConfigPath); if (File.Exists(text)) { JsonUtility.FromJsonOverwrite(File.ReadAllText(text), (object)Localization); } else { File.WriteAllText(text, JsonUtility.ToJson((object)Localization, true)); } if (Localization.PartnerInventoryText == "Partner's Inventory (Read Only)") { Localization.PartnerInventoryText = new StringLocalization().PartnerInventoryText; } } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is ArgumentException) { Localization = new StringLocalization(); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Cannot load localization at " + text + ": " + ex.Message + ". Using English. Your file is unchanged; correct its JSON or file permissions and restart.")); } } private void Update() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_handler != (Object)null && (_network != ZRoutedRpc.instance || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)InventoryGui.instance))) { _handler.Shutdown(); Object.Destroy((Object)(object)((Component)_handler).gameObject); _handler = null; } if (!((Object)(object)_handler != (Object)null) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)InventoryGui.instance) && ZRoutedRpc.instance != null) { _network = ZRoutedRpc.instance; _handler = new GameObject("PlayerTradingReforged").AddComponent<TradeHandler>(); } } private void OnDestroy() { if ((Object)(object)_handler != (Object)null) { _handler.Shutdown(); Object.Destroy((Object)(object)((Component)_handler).gameObject); } _harmony.UnpatchSelf(); _instance = null; } internal static void Warn(string message) { Plugin? instance = _instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)message); } } public static void NewLocalPlayer() { Plugin.OnLocalPlayerChanged?.Invoke(); } } public static class HUDTools { private static readonly Dictionary<GameObject, bool> States = new Dictionary<GameObject, bool>(); public static void SetHUDsActive(bool active) { if (active) { foreach (KeyValuePair<GameObject, bool> state in States) { if (Object.op_Implicit((Object)(object)state.Key)) { state.Key.SetActive(state.Value); } } States.Clear(); } else if (Object.op_Implicit((Object)(object)InventoryGui.instance) && States.Count == 0) { GameObject[] array = (GameObject[])(object)new GameObject[3] { ((Component)InventoryGui.instance.m_crafting).gameObject, ((Component)InventoryGui.instance.m_info).gameObject, ((Component)InventoryGui.instance.m_armor.transform.parent).gameObject }; foreach (GameObject val in array) { States[val] = val.activeSelf; val.SetActive(false); } } } } internal static class ZNetUtils { public static Player? GetPlayer(long peer) { foreach (Player allPlayer in Player.GetAllPlayers()) { if (Object.op_Implicit((Object)(object)allPlayer) && ((Character)allPlayer).GetOwner() == peer) { return allPlayer; } } return null; } public static Player? GetCharacter(long character) { foreach (Player allPlayer in Player.GetAllPlayers()) { if (Object.op_Implicit((Object)(object)allPlayer) && allPlayer.GetPlayerID() == character) { return allPlayer; } } return null; } } internal sealed class TradeHandler : MonoSingleton<TradeHandler> { private sealed class Request { public string Session { get; } public float Expires { get; } public Request(string session) { Session = session; Expires = Time.unscaledTime + 10f; } } private const string RequestRpc = "augusdogus.mods.PlayerTradingReforged.v1.Request"; private const string StartRpc = "augusdogus.mods.PlayerTradingReforged.v1.Start"; private const string MessageRpc = "augusdogus.mods.PlayerTradingReforged.v1.Message"; private const string RecoveryRpc = "augusdogus.mods.PlayerTradingReforged.v1.Recovery"; private const float MaxDistance = 5f; private readonly Dictionary<long, Request> _sent = new Dictionary<long, Request>(); private readonly Dictionary<long, Request> _received = new Dictionary<long, Request>(); private TradeInstance? _trade; private ZRoutedRpc? _network; private Player? _player; private float _nextRecovery; private bool _shutdown; private bool Available { get { if (_trade == null && (Object)(object)_player != (Object)null && !TradeRecovery.HasPending(_player)) { return !IsTradeWindowsOpen(); } return false; } } public bool CanEditOffer { get { if (_trade != null && _trade.Session.CanEdit) { return !MonoSingleton<TradeWindowManager>.Instance.IsInWindowPositionMode(); } return false; } } protected override void Init() { _network = ZRoutedRpc.instance; _player = Player.m_localPlayer; ((Component)this).gameObject.AddComponent<TradeWindowManager>(); _network.Register<string>("augusdogus.mods.PlayerTradingReforged.v1.Request", (Action<long, string>)ReceiveRequest); _network.Register<string>("augusdogus.mods.PlayerTradingReforged.v1.Start", (Action<long, string>)ReceiveStart); _network.Register<ZPackage>("augusdogus.mods.PlayerTradingReforged.v1.Message", (Action<long, ZPackage>)ReceiveMessage); _network.Register<string, string>("augusdogus.mods.PlayerTradingReforged.v1.Recovery", (Action<long, string, string>)ReceiveRecovery); Plugin.OnLocalPlayerChanged += OnNewLocalPlayer; } private void Update() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) if (_shutdown || (Object)(object)_player == (Object)null) { return; } _trade?.Tick(); if (_trade == null && Time.unscaledTime >= _nextRecovery) { _nextRecovery = Time.unscaledTime + 3f; RecoverItems(); } if (((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Console.IsVisible() || Menu.IsVisible() || TextInput.IsVisible()) { return; } if (Input.GetKeyDown(Plugin.EditWindowLayoutKey.Value)) { MonoSingleton<TradeWindowManager>.Instance.ToggleWindowPositionMode(); } if (_trade == null && !IsTradeWindowsOpen() && !InventoryGui.IsVisible() && !((Character)_player).IsDead() && !((Character)_player).IsTeleporting() && (ZInput.GetButtonDown("Use") || ZInput.GetButtonDown("JoyUse")) && (!Plugin.UseModifierKey.Value || Input.GetKey(Plugin.ModifierKey.Value))) { _player.UpdateHover(); Character hoveringCreature = _player.m_hoveringCreature; Player val = (Player)(object)((hoveringCreature is Player) ? hoveringCreature : null); if (val != null && CanTradeWith(val)) { RequestTrade(val); } } } private bool CanTradeWith(Player target) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_player != (Object)null && (Object)(object)target != (Object)(object)_player && Object.op_Implicit((Object)(object)target) && !((Character)target).IsDead() && !((Character)_player).IsDead() && !((Character)_player).IsTeleporting() && !((Character)target).IsTeleporting()) { return Vector3.Distance(((Component)_player).transform.position, ((Component)target).transform.position) <= 5f; } return false; } private static bool ValidSession(string id) { Guid result; if (id.Length == 32) { return Guid.TryParseExact(id, "N", out result); } return false; } private static bool Active(Dictionary<long, Request> requests, long peer, out Request? request) { if (requests.TryGetValue(peer, out Request value) && value.Expires >= Time.unscaledTime) { request = value; return true; } requests.Remove(peer); request = null; return false; } private void RequestTrade(Player target) { if (!Available) { Show(Plugin.Localization.CantStartNewTradeInstance); return; } long owner = ((Character)target).GetOwner(); Request request2; if (Active(_received, owner, out Request request) && request != null) { if (!((Object)(object)_player != (Object)null) || !Active(_sent, owner, out request2) || ((Character)_player).GetOwner() >= owner) { StartTrade(target, request.Session, coordinator: false); ZRoutedRpc? network = _network; if (network != null) { network.InvokeRoutedRPC(owner, "augusdogus.mods.PlayerTradingReforged.v1.Start", new object[1] { request.Session }); } } } else if (Active(_sent, owner, out request2)) { Show(Plugin.Localization.TradeRecentlySent); } else { Request request3 = new Request(Guid.NewGuid().ToString("N")); _sent[owner] = request3; ZRoutedRpc? network2 = _network; if (network2 != null) { network2.InvokeRoutedRPC(owner, "augusdogus.mods.PlayerTradingReforged.v1.Request", new object[1] { request3.Session }); } Show(Plugin.Localization.TradeRequestSent); } } private void ReceiveRequest(long sender, string session) { if (!ValidSession(session) || !Available) { return; } Player player = ZNetUtils.GetPlayer(sender); if (!((Object)(object)player == (Object)null) && CanTradeWith(player) && !Active(_received, sender, out Request _)) { _received[sender] = new Request(session); if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowBiomeFoundMsg(player.GetPlayerName() + " " + Plugin.Localization.XWantsToTrade, false); } } } private void ReceiveStart(long sender, string session) { if (Available && Active(_sent, sender, out Request request) && request != null && !(request.Session != session)) { Player player = ZNetUtils.GetPlayer(sender); if ((Object)(object)player != (Object)null && CanTradeWith(player)) { StartTrade(player, session, coordinator: true); } } } private void StartTrade(Player target, string session, bool coordinator) { if (!((Object)(object)_player == (Object)null)) { _sent.Clear(); _received.Clear(); _trade = new TradeInstance(this, _player, target, new TradeSession(session, ((Character)target).GetOwner(), coordinator)); _trade.Open(); Show(Plugin.Localization.StartedTradeWithX + " " + target.GetPlayerName()); } } private void ReceiveMessage(long sender, ZPackage package) { if (_trade == null || sender != _trade.Session.Peer || package.Size() > 524288) { return; } try { string id = package.ReadString(); if (_trade.Session.Matches(sender, id)) { string kind = package.ReadString(); int num = package.ReadInt(); int num2 = package.ReadInt(); string items = package.ReadString(); if (package.GetPos() != package.Size() || num < 0 || num2 < 0) { _trade.Cancel(); } else { _trade.Receive(kind, num, num2, items); } } } catch (Exception ex) when (ex is IOException || ex is ArgumentException || ex is OverflowException) { Plugin.Warn($"Rejected malformed trade message from {sender}: {ex.Message}"); _trade?.Cancel(); } } internal void Send(TradeSession session, string kind, string items = "") { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(session.Id); val.Write(kind); val.Write(session.LocalRevision); val.Write(session.RemoteRevision); val.Write(items); ZRoutedRpc? network = _network; if (network != null) { network.InvokeRoutedRPC(session.Peer, "augusdogus.mods.PlayerTradingReforged.v1.Message", new object[1] { val }); } } internal void Close(TradeInstance trade) { if (_trade == trade) { _trade = null; MonoSingleton<TradeWindowManager>.Instance.CancelInstance(); } } private void RecoverItems() { if ((Object)(object)_player == (Object)null || ((Character)_player).IsDead() || ((Character)_player).IsTeleporting()) { return; } TradeRecovery.Entry entry = TradeRecovery.Read(_player); if (entry == null || !ValidSession(entry.Session)) { return; } if (entry.State == TradeRecovery.RecoveryState.AwaitingDecision) { Player character = ZNetUtils.GetCharacter(entry.PeerCharacter); if ((Object)(object)character != (Object)null) { ZRoutedRpc? network = _network; if (network != null) { network.InvokeRoutedRPC(((Character)character).GetOwner(), "augusdogus.mods.PlayerTradingReforged.v1.Recovery", new object[2] { entry.Session, "query" }); } } } else { bool flag = entry.State == TradeRecovery.RecoveryState.CoordinatorOffer && TradeRecovery.Decision(_player, entry.Session, entry.PeerCharacter) == "commit"; if (entry.State == TradeRecovery.RecoveryState.CoordinatorOffer && !flag) { TradeRecovery.RecordDecision(_player, entry.Session, entry.PeerCharacter, committed: false); } if (TradeRecovery.Resolve(_player, entry, flag)) { Show(Plugin.Localization.ReturnedItems); } } } private void ReceiveRecovery(long sender, string session, string decision) { if ((Object)(object)_player == (Object)null || !ValidSession(session)) { return; } Player player = ZNetUtils.GetPlayer(sender); if ((Object)(object)player == (Object)null || (Object)(object)player == (Object)(object)_player) { return; } if (decision == "query") { if (_trade != null && _trade.Session.Matches(sender, session) && _trade.Session.IsCoordinator) { _trade.Cancel(); } string text = TradeRecovery.Decision(_player, session, player.GetPlayerID()); if (text != null) { ZRoutedRpc? network = _network; if (network != null) { network.InvokeRoutedRPC(sender, "augusdogus.mods.PlayerTradingReforged.v1.Recovery", new object[2] { session, text }); } } } else if (!((Character)_player).IsDead() && !((Character)_player).IsTeleporting() && (!(decision != "commit") || !(decision != "abort"))) { TradeRecovery.Entry entry = TradeRecovery.Read(_player); if (entry != null && entry.State == TradeRecovery.RecoveryState.AwaitingDecision && !(entry.Session != session) && entry.PeerCharacter == player.GetPlayerID()) { TradeRecovery.Resolve(_player, entry, decision == "commit"); Show((decision == "commit") ? Plugin.Localization.TradeSuccessful : Plugin.Localization.ReturnedItems); } } } public bool IsTradeWindowsOpen() { if (_trade == null) { if ((Object)(object)MonoSingleton<TradeWindowManager>.Current != (Object)null) { return MonoSingleton<TradeWindowManager>.Current.IsInWindowPositionMode(); } return false; } return true; } public bool InTradeInstance() { return _trade != null; } public Inventory? TryGetToTradeInventory() { return _trade?.Give; } public float GetMaxDistance() { return 5f; } public string GetAction(Player target) { if (!Active(_received, ((Character)target).GetOwner(), out Request _)) { return Plugin.Localization.RequestTrade; } return Plugin.Localization.AcceptRequest; } public void TryCancelTradeInstance() { _trade?.Cancel(); } public void TryCancelWindowEditMode() { MonoSingleton<TradeWindowManager>.Current?.DisableWindowPositionMode(); } public void OnNewLocalPlayer() { _trade?.Cancel(); _player = Player.m_localPlayer; _sent.Clear(); _received.Clear(); } internal static void Show(string message) { if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true); } } public void Shutdown() { if (_shutdown) { return; } _shutdown = true; _trade?.Cancel(); if ((Object)(object)MonoSingleton<TradeWindowManager>.Current != (Object)null) { MonoSingleton<TradeWindowManager>.Current.DisableWindowPositionMode(); } Plugin.OnLocalPlayerChanged -= OnNewLocalPlayer; if (_network != null) { string[] array = new string[4] { "augusdogus.mods.PlayerTradingReforged.v1.Request", "augusdogus.mods.PlayerTradingReforged.v1.Start", "augusdogus.mods.PlayerTradingReforged.v1.Message", "augusdogus.mods.PlayerTradingReforged.v1.Recovery" }; foreach (string text in array) { _network.m_functions.Remove(StringExtensionMethods.GetStableHashCode(text)); } } } private void OnDestroy() { Shutdown(); } } internal sealed class TradeInstance { private readonly TradeHandler _handler; private readonly Player _local; private readonly Player _peer; private readonly long _peerCharacter; private readonly TradeWindowManager _windows; private bool _closed; private float _prepareStarted; private float _lastHeartbeat; private float _nextHeartbeat; private float _nextInventoryPreview; private string? _lastInventoryPreview; private float? _lastCarryCapacity; private string? _lastWeights; private string? _lastReservedPreview; private bool _receivedReservedPreview; public TradeSession Session { get; } public Inventory Give { get; } private Inventory ReceiveItems { get; } public TradeInstance(TradeHandler handler, Player local, Player peer, TradeSession session) { _handler = handler; _local = local; _peer = peer; Session = session; _peerCharacter = peer.GetPlayerID(); _windows = MonoSingleton<TradeWindowManager>.Instance; Give = _windows.GetToTradeInventory(); ReceiveItems = _windows.GetToReceiveInventory(); _lastHeartbeat = Time.unscaledTime; } public void Open() { _windows.StartNewInstance(); _windows.SetPartnerName(_peer.GetPlayerName()); _windows.OnTradeAcceptPressed += Accept; _windows.OnChangeTradePressed += Change; _windows.OnCancelTradePressed += Cancel; Inventory give = Give; give.m_onChanged = (Action)Delegate.Combine(give.m_onChanged, new Action(OfferChanged)); Persist(); } private void Persist() { TradeRecovery.Store(_local, Session, _peerCharacter, Give, ReceiveItems); } private void RefreshAcceptance() { _windows.SetToTradeAccepted(Session.LocalAccepted); _windows.SetToReceiveAccepted(Session.RemoteAccepted); } public void OfferChanged() { if (!_closed && Session.ChangeOffer()) { Persist(); RefreshAcceptance(); _handler.Send(Session, "offer", TradeInventory.Save(Give)); } } private void Accept() { if (_closed || !TradeInventory.CanFit(((Humanoid)_local).GetInventory(), ReceiveItems)) { TradeHandler.Show(Plugin.Localization.NotEnoughInventorySlots); } else if (Session.Accept()) { InventoryGui.instance.SetupDragItem((ItemData)null, (Inventory)null, 1); RefreshAcceptance(); _handler.Send(Session, "accept"); TryPrepare(); } } private void Change() { if (Session.Change()) { Persist(); RefreshAcceptance(); _handler.Send(Session, "offer", TradeInventory.Save(Give)); } } private void TryPrepare() { if (Session.BeginPrepare()) { _prepareStarted = Time.unscaledTime; _handler.Send(Session, "prepare"); } } public void Receive(string kind, int theirs, int ours, string items) { if (_closed) { return; } _lastHeartbeat = Time.unscaledTime; if (kind == null) { return; } switch (kind.Length) { case 9: switch (kind[0]) { case 'h': _ = kind == "heartbeat"; break; case 'i': if (kind == "inventory" && !_receivedReservedPreview) { _windows.ShowPartnerInventory(TradeInventory.TryLoadPreview(items, out Inventory inventory) ? inventory : null); } break; } break; case 7: switch (kind[0]) { case 'w': if (kind == "weights") { _windows.SetPartnerWeights(TradeWeightSnapshot.Parse(items)); } break; case 'p': if (kind == "prepare") { if (!TradeInventory.CanFit(((Humanoid)_local).GetInventory(), ReceiveItems) || !Session.Prepare(theirs, ours)) { Cancel(); break; } Persist(); _prepareStarted = Time.unscaledTime; _handler.Send(Session, "ready"); } break; } break; case 5: switch (kind[0]) { case 'o': if (!(kind == "offer")) { break; } if (Session.State == TradeSession.Phase.Preparing) { Cancel(); } else if (theirs > Session.RemoteRevision && Session.State == TradeSession.Phase.Editing) { if (!TradeInventory.TryLoad(items, out Inventory inventory2)) { TradeHandler.Show(Plugin.Localization.TradeInvalid); Cancel(); } else if (Session.ReceiveOffer(theirs)) { TradeInventory.Replace(ReceiveItems, inventory2); Persist(); RefreshAcceptance(); _windows.RefreshToReceiveWindow(); } } break; case 'r': if (kind == "ready") { if (!Session.IsCoordinator || !TradeInventory.CanFit(((Humanoid)_local).GetInventory(), ReceiveItems)) { Cancel(); } else if (Session.Commit(theirs, ours)) { TradeRecovery.RecordDecision(_local, Session.Id, _peerCharacter, committed: true); _handler.Send(Session, "commit"); Finish(); } } break; case 'a': if (kind == "abort" && !Session.IsCoordinator && Session.Cancel(coordinatorAborted: true)) { Refund(localCancellation: false); } break; } break; case 6: switch (kind[1]) { case 'c': if (kind == "accept" && Session.ReceiveAccept(theirs, ours)) { RefreshAcceptance(); TryPrepare(); } break; case 'o': if (kind == "commit" && !Session.IsCoordinator && Session.Commit(theirs, ours)) { Finish(); } break; case 'a': if (kind == "cancel" && Session.IsCoordinator) { Cancel(); } break; } break; case 8: if (kind == "capacity") { _windows.SetPartnerCapacity((float.TryParse(items, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result) && !float.IsInfinity(result) && result >= 0f) ? new float?(result) : ((float?)null)); } break; case 22: if (kind == "inventory-reservations") { _receivedReservedPreview = true; _windows.ShowPartnerPreview(TradePreviewSnapshot.Parse(items)); } break; } } private void Finish() { _closed = true; _windows.ClearOfferedView(); Give.RemoveAll(); TradeRecovery.Entry entry = new TradeRecovery.Entry { Session = Session.Id, PeerCharacter = _peerCharacter, Give = TradeInventory.Save(ReceiveItems), Receive = TradeInventory.Save(ReceiveItems) }; TradeRecovery.Resolve(_local, entry, committed: true); TradeHandler.Show(Plugin.Localization.TradeSuccessful); Close(); } public void Tick() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (_closed) { return; } if (!Object.op_Implicit((Object)(object)_local) || !Object.op_Implicit((Object)(object)_peer) || ((Character)_local).IsDead() || ((Character)_peer).IsDead() || ((Character)_local).IsTeleporting() || ((Character)_peer).IsTeleporting() || Vector3.Distance(((Component)_local).transform.position, ((Component)_peer).transform.position) > _handler.GetMaxDistance() || Time.unscaledTime - _lastHeartbeat > 15f) { Cancel(); return; } if (Time.unscaledTime >= _nextHeartbeat) { _nextHeartbeat = Time.unscaledTime + 2f; _handler.Send(Session, "heartbeat"); } if (Time.unscaledTime >= _nextInventoryPreview) { _nextInventoryPreview = Time.unscaledTime + 0.5f; float maxCarryWeight = _local.GetMaxCarryWeight(); if (maxCarryWeight != _lastCarryCapacity) { _handler.Send(Session, "capacity", maxCarryWeight.ToString("R", CultureInfo.InvariantCulture)); _lastCarryCapacity = maxCarryWeight; } string text = TradeWeightSnapshot.Encode(((Humanoid)_local).GetInventory().GetTotalWeight(), Give.GetTotalWeight(), maxCarryWeight); if (text != _lastWeights) { _handler.Send(Session, "weights", text); _lastWeights = text; } string text2 = TradeInventory.SavePreview(((Humanoid)_local).GetInventory()); if (text2 != _lastInventoryPreview) { _handler.Send(Session, "inventory", text2); _lastInventoryPreview = text2; } string text3 = new TradePreviewSnapshot(((Humanoid)_local).GetInventory(), _windows.GetReservedInventory()).Encode(); if (text3 != _lastReservedPreview) { _handler.Send(Session, "inventory-reservations", text3); _lastReservedPreview = text3; } } if ((Session.State == TradeSession.Phase.Preparing || Session.State == TradeSession.Phase.Prepared) && Time.unscaledTime - _prepareStarted > 10f) { Cancel(); } } public void Cancel() { if (!_closed) { if (!Session.Cancel()) { Persist(); _closed = true; TradeHandler.Show(Plugin.Localization.TradePending); Give.RemoveAll(); Close(); } else { _handler.Send(Session, Session.IsCoordinator ? "abort" : "cancel"); Refund(localCancellation: true); } } } private void Refund(bool localCancellation) { _closed = true; _windows.ClearOfferedView(); if (Session.IsCoordinator) { TradeRecovery.RecordDecision(_local, Session.Id, _peerCharacter, committed: false); } TradeRecovery.Entry entry = new TradeRecovery.Entry { Session = Session.Id, PeerCharacter = _peerCharacter, Give = TradeInventory.Save(Give) }; TradeRecovery.Resolve(_local, entry, committed: false); Give.RemoveAll(); TradeHandler.Show(localCancellation ? Plugin.Localization.LocalPlayerCancelledTrade : Plugin.Localization.XHasCancelledTrade); Close(); } private void Close() { Inventory give = Give; give.m_onChanged = (Action)Delegate.Remove(give.m_onChanged, new Action(OfferChanged)); _windows.OnTradeAcceptPressed -= Accept; _windows.OnChangeTradePressed -= Change; _windows.OnCancelTradePressed -= Cancel; _handler.Close(this); } } } namespace PlayerTradingReforged.Trading { internal sealed class ReservationLedger<T> where T : class { internal sealed class Portion { public int Slot { get; } public int Count { get; set; } public Portion(int slot, int count) { Slot = slot; Count = count; } } private readonly Dictionary<T, List<Portion>> _items = new Dictionary<T, List<Portion>>(); public IEnumerable<int> Slots => (from part in _items.Values.SelectMany((List<Portion> parts) => parts) select part.Slot).Distinct(); public int CountAt(int slot) { return (from part in _items.Values.SelectMany((List<Portion> parts) => parts) where part.Slot == slot select part).Sum((Portion part) => part.Count); } public IEnumerable<int> Origins(T item) { if (!_items.TryGetValue(item, out List<Portion> value)) { return Enumerable.Empty<int>(); } return value.Select((Portion part) => part.Slot).Distinct(); } public void Clear() { _items.Clear(); } public void Update(IReadOnlyDictionary<T, int> before, IReadOnlyDictionary<T, int> after, int? sourceSlot) { Queue<Portion> queue = new Queue<Portion>(); foreach (KeyValuePair<T, int> item in before) { int value; int num = item.Value - (after.TryGetValue(item.Key, out value) ? value : 0); if (num <= 0 || !_items.TryGetValue(item.Key, out List<Portion> value2)) { continue; } while (num > 0 && value2.Count > 0) { Portion portion = value2[0]; int num2 = Math.Min(num, portion.Count); queue.Enqueue(new Portion(portion.Slot, num2)); portion.Count -= num2; num -= num2; if (portion.Count == 0) { value2.RemoveAt(0); } } if (value2.Count == 0) { _items.Remove(item.Key); } } int value5; int num3 = after.Sum<KeyValuePair<T, int>>((KeyValuePair<T, int> pair) => Math.Max(0, pair.Value - (before.TryGetValue(pair.Key, out value5) ? value5 : 0))) - queue.Sum((Portion part) => part.Count); if (sourceSlot.HasValue && num3 > 0) { queue.Enqueue(new Portion(sourceSlot.Value, num3)); } foreach (KeyValuePair<T, int> item2 in after) { int value3; int num4 = item2.Value - (before.TryGetValue(item2.Key, out value3) ? value3 : 0); if (num4 <= 0) { continue; } if (!_items.TryGetValue(item2.Key, out List<Portion> value4)) { value4 = new List<Portion>(); _items.Add(item2.Key, value4); } while (num4 > 0 && queue.Count > 0) { Portion portion2 = queue.Peek(); int num5 = Math.Min(num4, portion2.Count); value4.Add(new Portion(portion2.Slot, num5)); portion2.Count -= num5; num4 -= num5; if (portion2.Count == 0) { queue.Dequeue(); } } } } } internal static class TradeCapacity { internal readonly struct Stack { public string Name { get; } public int Quality { get; } public int WorldLevel { get; } public int Count { get; } public int Maximum { get; } public Stack(string name, int quality, int worldLevel, int count, int maximum) { Name = name; Quality = quality; WorldLevel = worldLevel; Count = count; Maximum = maximum; } public Stack WithCount(int count) { return new Stack(Name, Quality, WorldLevel, count, Maximum); } } public static bool CanFit(int slots, IReadOnlyList<Stack> existing, IReadOnlyList<Stack> incoming) { List<Stack> list = new List<Stack>(existing); foreach (Stack item in incoming) { if (item.Count <= 0 || item.Maximum <= 0 || item.Count > item.Maximum) { return false; } int num = item.Count; if (item.Maximum > 1) { for (int i = 0; i < list.Count; i++) { if (num <= 0) { break; } Stack stack = list[i]; if (!(stack.Name != item.Name) && stack.Quality == item.Quality && stack.WorldLevel == item.WorldLevel) { int num2 = Math.Min(num, Math.Max(0, stack.Maximum - stack.Count)); list[i] = stack.WithCount(stack.Count + num2); num -= num2; } } } if (num != 0) { if (list.Count >= slots) { return false; } list.Add(item.WithCount(num)); } } return true; } } internal static class TradeInventory { private enum Purpose { Offer, Preview } public const int Width = 6; public const int Height = 4; public const int MaxPackageBytes = 262144; private const int MaxPreviewWidth = 16; private const int MaxPreviewHeight = 32; public static Inventory Create() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown return new Inventory("Trade", (Sprite)null, 6, 4); } public static string Save(Inventory inventory) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); inventory.Save(val); return val.GetBase64(); } public static bool TryLoad(string encoded, out Inventory inventory) { return TryLoad(encoded, Purpose.Offer, out inventory); } public static string SavePreview(Inventory inventory) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (!ValidPreviewDimensions(inventory.GetWidth(), inventory.GetHeight())) { return ""; } ZPackage val = new ZPackage(); val.Write(inventory.GetWidth()); val.Write(inventory.GetHeight()); inventory.Save(val); if (val.Size() > 262144) { return ""; } return val.GetBase64(); } public static bool TryLoadPreview(string encoded, out Inventory inventory) { return TryLoad(encoded, Purpose.Preview, out inventory); } private static bool ValidPreviewDimensions(int width, int height) { if (width >= 1 && width <= 16 && height >= 1) { return height <= 32; } return false; } private static bool TryLoad(string encoded, Purpose purpose, out Inventory inventory) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0213: Unknown result type (might be due to invalid IL or missing references) inventory = Create(); if (encoded.Length == 0 || encoded.Length > 349529) { return false; } try { ZPackage val = new ZPackage(encoded); if (val.Size() > 262144) { return false; } int num = 6; int num2 = 4; if (purpose == Purpose.Preview) { num = val.ReadInt(); num2 = val.ReadInt(); if (!ValidPreviewDimensions(num, num2)) { return false; } inventory = new Inventory("Preview", (Sprite)null, num, num2); } ZPackage val2 = new ZPackage(); Create().Save(val2); val2.SetPos(0); int num3 = val2.ReadInt(); if (val.ReadInt() != num3) { return false; } int num4 = val.ReadUShort(); if (num4 > num * num2) { return false; } HashSet<Vector2i> hashSet = new HashSet<Vector2i>(); ItemDrop val3 = default(ItemDrop); for (int i = 0; i < num4; i++) { ValueTuple<int, ItemData> valueTuple = ItemData.Load(val, (Item)num3); int item = valueTuple.Item1; ItemData item2 = valueTuple.Item2; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item); if ((Object)(object)itemPrefab == (Object)null || !itemPrefab.TryGetComponent<ItemDrop>(ref val3)) { return false; } item2.m_shared = val3.m_itemData.m_shared; item2.m_dropPrefab = itemPrefab; if (item2.m_stack <= 0 || item2.m_stack > item2.m_shared.m_maxStackSize || (purpose == Purpose.Offer && item2.m_shared.m_questItem) || item2.m_quality < 1 || item2.m_quality > item2.m_shared.m_maxQuality || item2.m_variant < 0 || item2.m_variant >= item2.m_shared.m_icons.Length || float.IsNaN(item2.m_durability) || float.IsInfinity(item2.m_durability) || item2.m_durability < 0f || item2.m_gridPos.x < 0 || item2.m_gridPos.x >= num || item2.m_gridPos.y < 0 || item2.m_gridPos.y >= num2 || !hashSet.Add(item2.m_gridPos)) { return false; } if (purpose == Purpose.Offer) { item2.m_equipped = false; } inventory.m_inventory.Add(item2); } if (val.GetPos() != val.Size()) { return false; } inventory.Changed(false, false); return true; } catch (Exception ex) when (ex is FormatException || ex is IOException || ex is ArgumentException || ex is OverflowException) { Plugin.Warn("Rejected unreadable trade inventory: " + ex.Message); return false; } } public static bool CanFit(Inventory destination, Inventory incoming) { return TradeCapacity.CanFit(destination.GetWidth() * destination.GetHeight(), destination.GetAllItems().Select(Describe).ToArray(), incoming.GetAllItems().Select(Describe).ToArray()); static TradeCapacity.Stack Describe(ItemData item) { return new TradeCapacity.Stack(item.m_shared.m_name, item.m_quality, item.m_worldLevel, item.m_stack, item.m_shared.m_maxStackSize); } } public static void Replace(Inventory target, Inventory source) { target.RemoveAll(); foreach (ItemData allItem in source.GetAllItems()) { target.m_inventory.Add(allItem.Clone()); } target.Changed(false, false); } } internal sealed class TradePreviewSnapshot { public Inventory Available { get; } public Inventory Reserved { get; } public TradePreviewSnapshot(Inventory available, Inventory reserved) { Available = available; Reserved = reserved; } public string Encode() { string text = TradeInventory.SavePreview(Available); string text2 = TradeInventory.SavePreview(Reserved); string text3 = text + "|" + text2; if (text.Length <= 0 || text2.Length <= 0 || text3.Length > 524288) { return ""; } return text3; } public static TradePreviewSnapshot? Parse(string text) { if (text.Length > 524288) { return null; } int num = text.IndexOf('|'); if (num < 1 || text.IndexOf('|', num + 1) >= 0 || !TradeInventory.TryLoadPreview(text.Substring(0, num), out Inventory inventory) || !TradeInventory.TryLoadPreview(text.Substring(num + 1), out Inventory inventory2) || inventory.GetWidth() != inventory2.GetWidth() || inventory.GetHeight() != inventory2.GetHeight()) { return null; } foreach (ItemData allItem in inventory2.GetAllItems()) { if (allItem.m_equipped || allItem.m_shared.m_questItem) { return null; } ItemData itemAt = inventory.GetItemAt(allItem.m_gridPos.x, allItem.m_gridPos.y); if (itemAt != null && (!itemAt.IsSameType(allItem) || allItem.m_stack > itemAt.m_shared.m_maxStackSize - itemAt.m_stack)) { return null; } } return new TradePreviewSnapshot(inventory, inventory2); } } internal static class TradeRecovery { internal enum RecoveryState { Refund, AwaitingDecision, CoordinatorOffer } [Serializable] internal sealed class Entry { public string Session = ""; public long PeerCharacter; public RecoveryState State; public string Give = ""; public string Receive = ""; } private const string ActiveKey = "augusdogus.mods.PlayerTradingReforged.escrow.v1"; private const string ReceiptPrefix = "augusdogus.mods.PlayerTradingReforged.receipt.v1."; public static void Store(Player player, TradeSession session, long peerCharacter, Inventory give, Inventory receive) { player.m_customData["augusdogus.mods.PlayerTradingReforged.escrow.v1"] = JsonUtility.ToJson((object)new Entry { Session = session.Id, PeerCharacter = peerCharacter, State = ((session.State == TradeSession.Phase.Prepared) ? RecoveryState.AwaitingDecision : (session.IsCoordinator ? RecoveryState.CoordinatorOffer : RecoveryState.Refund)), Give = TradeInventory.Save(give), Receive = TradeInventory.Save(receive) }); } public static Entry? Read(Player player) { if (!player.m_customData.TryGetValue("augusdogus.mods.PlayerTradingReforged.escrow.v1", out var value)) { return null; } try { Entry entry = JsonUtility.FromJson<Entry>(value); if (entry != null && Guid.TryParseExact(entry.Session, "N", out var _) && entry.PeerCharacter != 0L && Enum.IsDefined(typeof(RecoveryState), entry.State)) { return entry; } } catch (ArgumentException ex) { Plugin.Warn("Cannot read saved trade: " + ex.Message); } Plugin.Warn("Saved trade recovery data is invalid. New trades remain blocked; preserve the character save and inspect its PlayerTradingReforged custom data before recovery."); return null; } public static bool HasPending(Player player) { return player.m_customData.ContainsKey("augusdogus.mods.PlayerTradingReforged.escrow.v1"); } public static void Clear(Player player) { player.m_customData.Remove("augusdogus.mods.PlayerTradingReforged.escrow.v1"); } public static void RecordDecision(Player player, string session, long peerCharacter, bool committed) { player.m_customData["augusdogus.mods.PlayerTradingReforged.receipt.v1." + session] = peerCharacter.ToString(CultureInfo.InvariantCulture) + (committed ? ":commit" : ":abort"); } public static string? Decision(Player player, string session, long peerCharacter) { if (!player.m_customData.TryGetValue("augusdogus.mods.PlayerTradingReforged.receipt.v1." + session, out var value)) { return null; } string text = peerCharacter.ToString(CultureInfo.InvariantCulture) + ":"; if (!value.StartsWith(text, StringComparison.Ordinal)) { return null; } return value.Substring(text.Length); } public static bool Resolve(Player player, Entry entry, bool committed) { entry.Give = (committed ? entry.Receive : entry.Give); entry.Receive = ""; entry.State = RecoveryState.Refund; player.m_customData["augusdogus.mods.PlayerTradingReforged.escrow.v1"] = JsonUtility.ToJson((object)entry); if (!TradeInventory.TryLoad(entry.Give, out Inventory inventory)) { Plugin.Warn("Saved trade items could not be read. Escrow is preserved in character custom data; restore the matching item mods before retrying."); return false; } ((Humanoid)player).GetInventory().MoveAll(inventory); if (inventory.NrOfItems() != 0) { entry.State = RecoveryState.Refund; entry.Give = TradeInventory.Save(inventory); entry.Receive = TradeInventory.Save(TradeInventory.Create()); player.m_customData["augusdogus.mods.PlayerTradingReforged.escrow.v1"] = JsonUtility.ToJson((object)entry); return false; } Clear(player); return true; } } internal sealed class TradeSession { internal enum Phase { Editing, Preparing, Prepared, Committed, Cancelled } public string Id { get; } public long Peer { get; } public bool IsCoordinator { get; } public Phase State { get; private set; } public int LocalRevision { get; private set; } public int RemoteRevision { get; private set; } public bool LocalAccepted { get; private set; } public bool RemoteAccepted { get; private set; } public bool CanEdit => State == Phase.Editing; public bool BothAccepted { get { if (LocalAccepted) { return RemoteAccepted; } return false; } } public TradeSession(string id, long peer, bool coordinator) { Id = id; Peer = peer; IsCoordinator = coordinator; } public bool Matches(long sender, string id) { if (sender == Peer) { return id == Id; } return false; } public bool ChangeOffer() { if (State != Phase.Editing) { return false; } LocalRevision++; ResetAcceptance(); return true; } public bool ReceiveOffer(int revision) { if (State != Phase.Editing || revision <= RemoteRevision) { return false; } RemoteRevision = revision; ResetAcceptance(); return true; } public bool Accept() { if (State != Phase.Editing) { return false; } LocalAccepted = true; return true; } public bool ReceiveAccept(int theirRevision, int ourRevision) { if (State != Phase.Editing || !MatchesRevisions(theirRevision, ourRevision)) { return false; } RemoteAccepted = true; return true; } public bool Change() { if (State != Phase.Editing) { return false; } return ChangeOffer(); } public bool BeginPrepare() { if (!IsCoordinator || State != Phase.Editing || !BothAccepted) { return false; } State = Phase.Preparing; return true; } public bool Prepare(int theirRevision, int ourRevision) { if (IsCoordinator || !BothAccepted || !MatchesRevisions(theirRevision, ourRevision)) { return false; } if (State == Phase.Prepared) { return true; } if (State != Phase.Editing) { return false; } State = Phase.Prepared; return true; } public bool Commit(int theirRevision, int ourRevision) { if (!MatchesRevisions(theirRevision, ourRevision)) { return false; } if (State != (Phase)(IsCoordinator ? 1 : 2)) { return false; } State = Phase.Committed; return true; } public bool Cancel(bool coordinatorAborted = false) { if (State == Phase.Committed || State == Phase.Cancelled) { return false; } if (State == Phase.Prepared && !coordinatorAborted) { return false; } State = Phase.Cancelled; return true; } private bool MatchesRevisions(int theirs, int ours) { if (theirs == RemoteRevision) { return ours == LocalRevision; } return false; } private void ResetAcceptance() { LocalAccepted = false; RemoteAccepted = false; } } internal sealed class TradeWeightSnapshot { public float Current { get; } public float InBag { get; } public float Capacity { get; } private TradeWeightSnapshot(float current, float inBag, float capacity) { Current = current; InBag = inBag; Capacity = capacity; } public static string Encode(float inBag, float offered, float capacity) { return (inBag + offered).ToString("R", CultureInfo.InvariantCulture) + ";" + inBag.ToString("R", CultureInfo.InvariantCulture) + ";" + capacity.ToString("R", CultureInfo.InvariantCulture); } public static TradeWeightSnapshot? Parse(string text) { if (text.Length > 128) { return null; } string[] array = text.Split(';'); if (array.Length != 3 || !Read(array[0], out var value) || !Read(array[1], out var value2) || !Read(array[2], out var value3) || value < value2) { return null; } return new TradeWeightSnapshot(value, value2, value3); } private static bool Read(string text, out float value) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value) && !float.IsInfinity(value)) { return value >= 0f; } return false; } } } namespace PlayerTradingReforged.Patterns { public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> { private static T? _instance; public static T? Current { get { if (!((Object)(object)_instance != (Object)null)) { return null; } return _instance; } } public static T Instance => Current ?? throw new InvalidOperationException(typeof(T).Name + " has not initialized."); private void Awake() { if ((Object)(object)Current != (Object)null) { Object.Destroy((Object)(object)this); } else if (this is T instance) { _instance = instance; Init(); } } protected virtual void Init() { } } } namespace PlayerTradingReforged.Patches { internal static class InventoryGuiPatches { [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] private static class UpdateContainer { private static bool Prefix(InventoryGui __instance) { if (!Active || (Object)(object)__instance.m_currentContainer != (Object)null) { return true; } Inventory val = Offer ?? MonoSingleton<TradeWindowManager>.Instance.GetToTradeInventory(); if (__instance.m_animator.GetBool("visible")) { ((Component)__instance.m_container).gameObject.SetActive(true); __instance.m_containerGrid.UpdateInventory(val, (Player)null, __instance.m_dragItem); __instance.m_containerName.text = val.GetName(); if (__instance.m_firstContainerUpdate) { __instance.m_containerGrid.ResetView(); __instance.m_firstContainerUpdate = false; } } return false; } } [HarmonyPatch(typeof(InventoryGui), "UpdateContainerWeight")] private static class UpdateWeight { private static bool Prefix(InventoryGui __instance) { if (!Active || (Object)(object)__instance.m_currentContainer != (Object)null) { return true; } Inventory val = Offer ?? MonoSingleton<TradeWindowManager>.Instance.GetToTradeInventory(); __instance.m_containerWeight.text = Mathf.CeilToInt(val.GetTotalWeight()).ToString(); return false; } } [HarmonyPatch(typeof(InventoryGui), "IsContainerOpen")] private static class IsContainerOpen { private static void Postfix(ref bool __result) { if (Active) { __result = true; } } } [HarmonyPatch(typeof(InventoryGui), "Update")] private static class KeepOpenOnUse { private static void Prefix() { if (Active) { ZInput.ResetButtonStatus("Use"); ZInput.ResetButtonStatus("JoyUse"); } } } [HarmonyPatch(typeof(InventoryGui), "Hide")] private static class CancelOnHide { private static void Prefix() { if (Active) { MonoSingleton<TradeHandler>.Current?.TryCancelTradeInstance(); MonoSingleton<TradeHandler>.Current?.TryCancelWindowEditMode(); } } } [HarmonyPatch(typeof(InventoryGui), "OnTakeAll")] private static class TakeAll { private static bool Prefix(InventoryGui __instance) { if (!Active || (Object)(object)__instance.m_currentContainer != (Object)null) { return true; } if (Editable) { Inventory offer = Offer; if (offer != null && !((Character)Player.m_localPlayer).IsTeleporting()) { __instance.SetupDragItem((ItemData)null, (Inventory)null, 1); ItemData[] array = offer.GetAllItems().ToArray(); foreach (ItemData val in array) { ((Humanoid)Player.m_localPlayer).GetInventory().MoveItemToThis(offer, val); } } } return false; } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class SelectItem { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Modifier mod) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if (!Active || (Object)(object)__instance.m_currentContainer != (Object)null) { return true; } if (Editable) { Inventory offer = Offer; if (offer != null) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting()) { return false; } if (grid.GetInventory() != offer && grid.GetInventory() != ((Humanoid)localPlayer).GetInventory()) { return false; } if (Object.op_Implicit((Object)(object)__instance.m_dragGo) || (int)mod != 2) { return true; } if (item == null || item.m_shared.m_questItem) { return false; } ((Humanoid)localPlayer).RemoveEquipAction(item); ((Humanoid)localPlayer).UnequipItem(item, true); ((grid.GetInventory() == offer) ? ((Humanoid)localPlayer).GetInventory() : offer).MoveItemToThis(grid.GetInventory(), item); __instance.m_moveItemEffects.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); return false; } } return false; } } [HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")] private static class UseItem { private static bool Prefix(InventoryGrid grid) { if (Active) { if (Editable) { return grid.GetInventory() == ((Humanoid)Player.m_localPlayer).GetInventory(); } return false; } return true; } } [HarmonyPatch(typeof(InventoryGui), "OnDropOutside")] private static class DropItem { private static bool Prefix() { if (Active) { return Editable; } return true; } } [HarmonyPatch(typeof(InventoryGui), "UpdateGamepad")] private static class Gamepad { private static bool Prefix(InventoryGui __instance) { if (!Active || (Object)(object)__instance.m_currentContainer != (Object)null) { return true; } if (__instance.m_inventoryGroup.IsActive) { if (ZInput.GetButtonDown("JoyTabLeft")) { __instance.SetActiveGroup(1, true); } if (ZInput.GetButtonDown("JoyTabRight")) { __instance.SetActiveGroup(0, true); } if (__instance.m_activeGroup != 0 && __instance.m_activeGroup != 1) { __instance.SetActiveGroup(1, true); } } return false; } } private static bool Active { get { if ((Object)(object)MonoSingleton<TradeHandler>.Current != (Object)null) { return MonoSingleton<TradeHandler>.Current.IsTradeWindowsOpen(); } return false; } } private static Inventory? Offer => MonoSingleton<TradeHandler>.Current?.TryGetToTradeInventory(); private static bool Editable { get { if ((Object)(object)MonoSingleton<TradeHandler>.Current != (Object)null) { return MonoSingleton<TradeHandler>.Current.CanEditOffer; } return false; } } } internal static class MessageHudPatches { [HarmonyPatch(typeof(MessageHud), "UpdateBiomeFound")] private static class MessageWrapping { private static void Postfix(MessageHud __instance) { if (!((Object)(object)__instance.m_biomeMsgInstance == (Object)null)) { TMP_Text componentInChildren = __instance.m_biomeMsgInstance.GetComponentInChildren<TMP_Text>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.overflowMode = (TextOverflowModes)0; } } } } } internal static class PlayerPatches { [HarmonyPatch(typeof(Player), "GetHoverText")] private static class Hover { private static void Postfix(Player __instance, ref string __result) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) TradeHandler current = MonoSingleton<TradeHandler>.Current; if (!((Object)(object)current == (Object)null) && !current.IsTradeWindowsOpen() && !((Object)(object)__instance == (Object)(object)Player.m_localPlayer)) { string text = (Plugin.UseModifierKey.Value ? (((object)Plugin.ModifierKey.Value/*cast due to .constrained prefix*/).ToString() + " + ") : ""); __result += Localization.instance.Localize("\n[<color=yellow><b>" + text + "$KEY_Use</b></color>] " + current.GetAction(__instance)); } } } [HarmonyPatch(typeof(Player), "SetLocalPlayer")] private static class LocalPlayer { private static void Postfix() { Plugin.NewLocalPlayer(); } } [HarmonyPatch(typeof(Player), "OnDeath")] private static class Death { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { MonoSingleton<TradeHandler>.Current?.TryCancelTradeInstance(); } } } } internal static class TradeReservationPatches { [HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[] { typeof(Inventory), typeof(ItemData) })] private static class QuickMove { private static void Prefix(Inventory __instance, Inventory fromInventory, ItemData item, out OfferedInventoryView.Transfer? __state) { __state = View?.Begin(__instance, fromInventory, item); } private static void Finalizer(OfferedInventoryView.Transfer? __state) { if (__state != null) { View?.End(__state); } } } [HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) })] private static class MoveToSlot { private static void Prefix(Inventory __instance, Inventory fromInventory, ItemData item, out OfferedInventoryView.Transfer? __state) { __state = View?.Begin(__instance, fromInventory, item); } private static void Finalizer(OfferedInventoryView.Transfer? __state) { if (__state != null) { View?.End(__state); } } } [HarmonyPatch(typeof(Inventory), "FindEmptySlot")] private static class EmptySlot { private static bool Prefix(Inventory __instance, bool topFirst, ref Vector2i __result) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) OfferedInventoryView view = View; if (view == null || !view.IsPlayerInventory(__instance)) { return true; } __result = view.FindEmptySlot(topFirst); return false; } } [HarmonyPatch(typeof(Inventory), "GetEmptySlots")] private static class EmptyCount { private static void Postfix(Inventory __instance, ref int __result) { OfferedInventoryView view = View; if (view != null && view.IsPlayerInventory(__instance)) { __result = Math.Max(0, __result - view.EmptyReservedSlots); } } } [HarmonyPatch(typeof(Inventory), "HaveEmptySlot")] private static class HasSlot { private static void Postfix(Inventory __instance, ref bool __result) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) OfferedInventoryView view = View; if (view != null && view.IsPlayerInventory(__instance)) { __result = view.FindEmptySlot(topFirst: true).x >= 0; } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] private static class AddToSlot { private static bool Prefix(Inventory __instance, int x, int y, ref bool __result) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) OfferedInventoryView view = View; if (view == null || !view.IsPlayerInventory(__instance) || view.CanAdd(new Vector2i(x, y))) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "FindFreeStackItem")] private static class FreeStack { private static void Postfix(Inventory __instance, string name, int quality, float worldLevel, ref ItemData? __result) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) OfferedInventoryView view = View; if (view == null || !view.IsPlayerInventory(__instance) || __result == null || view.CanAdd(__result.m_gridPos)) { return; } __result = null; foreach (ItemData allItem in __instance.GetAllItems()) { if (allItem.m_shared.m_name == name && allItem.m_quality == quality && (float)allItem.m_worldLevel == worldLevel && allItem.m_stack < allItem.m_shared.m_maxStackSize && view.CanAdd(allItem.m_gridPos)) { __result = allItem; break; } } } } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] private static class DropOnReservation { private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, Vector2i pos, ref bool __result) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) OfferedInventoryView view = View; if (view == null) { return true; } Inventory inventory = __instance.GetInventory(); ItemData itemAt = inventory.GetItemAt(pos.x, pos.y); if ((view.IsOfferInventory(inventory) || view.IsOfferInventory(fromInventory)) && itemAt != null && itemAt != item && (!itemAt.IsSameType(item) || itemAt.m_shared.m_maxStackSize == 1)) { __result = false; return false; } if (!view.IsPlayerInventory(inventory) || !view.Reserved(pos)) { return true; } if (!view.IsPlayerInventory(fromInventory) && view.CanReturn(item, pos)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] private static class DrawReservations { private static void Postfix(InventoryGrid __instance) { OfferedInventoryView view = View; if (view != null && (Object)(object)__instance == (Object)(object)InventoryGui.instance.m_playerGrid) { view.Draw(__instance); } } } private static OfferedInventoryView? View => MonoSingleton<TradeWindowManager>.Current?.OfferedView; } } namespace PlayerTradingReforged.GUI { internal sealed class ContainerTradeWindow : TradeWindow { private bool _flipped; private RectTransform? _takeAll; private RectTransform? _gridRoot; private GameObject? _stackAll; private bool _stackAllWasActive; private Transform? _originalParent; private int _originalSibling; public void Initialize() { InventoryGui instance = InventoryGui.instance; Initialize(instance.m_container, Plugin.Localization.ToGiveWindowText, left: true, Plugin.ToGiveUserOffset); _originalParent = ((Transform)instance.m_container).parent; _originalSibling = ((Transform)instance.m_container).GetSiblingIndex(); _takeAll = ((Component)instance.m_takeAllButton).GetComponent<RectTransform>(); _gridRoot = ((Component)((Component)base.Grid).transform.Find("Root")).GetComponent<RectTransform>(); _stackAll = ((Component)instance.m_stackAllButton).gameObject; } public override void Show() { ((Transform)base.Panel).SetParent(InventoryGui.instance.m_inventoryRoot, false); if (!_flipped) { Flip(); } base.Show(); if ((Object)(object)_stackAll != (Object)null) { _stackAllWasActive = _stackAll.activeSelf; _stackAll.SetActive(false); } InventoryGui.instance.m_containerName.text = base.Inventory.GetName(); InventoryGui.instance.Show((Container)null, 1); } public override void Hide() { base.Hide(); if (_flipped && Object.op_Implicit((Object)(object)InventoryGui.instance)) { Flip(); } if ((Object)(object)_originalParent != (Object)null && Object.op_Implicit((Object)(object)InventoryGui.instance)) { ((Transform)base.Panel).SetParent(_originalParent, false); ((Transform)base.Panel).SetSiblingIndex(_originalSibling); } RestorePosition(); if ((Object)(object)_stackAll != (Object)null) { _stackAll.SetActive(_stackAllWasActive); } if (Object.op_Implicit((Object)(object)InventoryGui.instance)) { InventoryGui.instance.CloseContainer(); InventoryGui.instance.Hide(); } } private void Flip() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_takeAll == (Object)null) && !((Object)(object)_gridRoot == (Object)null)) { Vector2 anchoredPosition = _takeAll.anchoredPosition; RectTransformUtility.FlipLayoutOnAxis(base.Panel, 0, true, true); RectTransformUtility.FlipLayoutOnAxis(_gridRoot, 0, true, true); _takeAll.anchoredPosition = anchoredPosition; _flipped = !_flipped; } } public override void Reset() { if (base.Inventory.NrOfItems() != 0) { throw new InvalidOperationException("Cannot open a trade before its previous items are recovered."); } base.Reset(); } } internal sealed class OfferedInventoryView { internal sealed class Transfer { public Dictionary<ItemData, int> Before { get; } public int? SourceSlot { get; } public ItemData? PreviousReturn { get; } public Transfer(Dictionary<ItemData, int> before, int? sourceSlot, ItemData? previousReturn) { Before = before; SourceSlot = sourceSlot; PreviousReturn = previousReturn; } } private readonly Inventory _player; private readonly Inventory _offer; private readonly ReservationLedger<ItemData> _ledger = new ReservationLedger<ItemData>(); private readonly Dictionary<int, ItemData> _templates = new Dictionary<int, ItemData>(); private Dictionary<ItemData, int> _last = new Dictionary<ItemData, int>(); private ItemData? _returning; public int EmptyReservedSlots => _ledger.Slots.Count((int slot) => _player.GetItemAt(Position(slot).x, Position(slot).y) == null); public OfferedInventoryView(Inventory player, Inventory offer) { _player = player; _offer = offer; } public bool IsPlayerInventory(Inventory inventory) { return inventory == _player; } public bool IsOfferInventory(Inventory inventory) { return inventory == _offer; } private int Slot(Vector2i position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return position.y * _player.GetWidth() + position.x; } private Vector2i Position(int slot) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) return new Vector2i(slot % _player.GetWidth(), slot / _player.GetWidth()); } private Dictionary<ItemData, int> Snapshot() { return _offer.GetAllItems().ToDictionary((ItemData item) => item, (ItemData item) => item.m_stack); } public bool Reserved(Vector2i position) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return _ledger.CountAt(Slot(position)) > 0; } public bool CanReturn(ItemData item, Vector2i position) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return _ledger.Origins(item).Contains(Slot(position)); } public bool CanAdd(Vector2i position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Reserved(position)) { if (_returning != null) { return CanReturn(_returning, position); } return false; } return true; } public Transfer? Begin(Inventory destination, Inventory source, ItemData item) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (source != _offer && destination != _offer) { return null; } Reconcile(); int? sourceSlot = ((source == _player && destination == _offer) ? new int?(Slot(item.m_gridPos)) : ((int?)null)); if (sourceSlot.HasValue) { _templates[sourceSlot.Value] = item.Clone(); } Transfer result = new Transfer(Snapshot(), sourceSlot, _returning); if (source == _offer && destination == _player) { _returning = item; } return result; } public void End(Transfer transfer) { try { Dictionary<ItemData, int> dictionary = Snapshot(); _ledger.Update(transfer.Before, dictionary, transfer.SourceSlot); _last = dictionary; } finally { _returning = transfer.PreviousReturn; } } private void Reconcile() { Dictionary<ItemData, int> dictionary = Snapshot(); _ledger.Update(_last, dictionary, null); _last = dictionary; } public Vector2i FindEmptySlot(bool topFirst) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if (_returning != null) { foreach (int item in _ledger.Origins(_returning)) { Vector2i val = Position(item); if (_player.GetItemAt(val.x, val.y) == null) { return val; } } } for (int i = 0; i < _player.GetHeight(); i++) { int num = (topFirst ? i : (_player.GetHeight() - 1 - i)); for (int j = 0; j < _player.GetWidth(); j++) { if (_player.GetItemAt(j, num) == null && !Reserved(new Vector2i(j, num))) { return new Vector2i(j, num); } } } return new Vector2i(-1, -1); } public Inventory CreateReservations() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) Reconcile(); Inventory val = new Inventory("Reservations", (Sprite)null, _player.GetWidth(), _player.GetHeight()); foreach (int slot in _ledger.Slots) { if (_templates.TryGetValue(slot, out ItemData value)) { ItemData val2 = value.Clone(); val2.m_stack = _ledger.CountAt(slot); val2.m_gridPos = Position(slot); val2.m_equipped = false; val.m_inventory.Add(val2); } } val.Changed(false, false); return val; } public void Draw(InventoryGrid grid) { ReservedInventoryRenderer.Draw(grid, _player, CreateReservations()); } public void Clear() { _ledger.Clear(); _templates.Clear(); _last.Clear(); _returning = null; } } internal sealed class PlayerInventoryFrame { public const float HeaderHeight = 50f; private readonly GameObject _original; private readonly RectTransform _frame; private bool _wasActive; public PlayerInventoryFrame(RectTransform player, RectTransform partner) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) _original = ((Component)((Transform)player).Find("Bkg")).gameObject; _frame = Object.Instantiate<RectTransform>(((Component)((Transform)partner).Find("Bkg")).GetComponent<RectTransform>(), (Transform)(object)player, false); ((Object)_frame).name = "PlayerTradingReforgedPlayerFrame"; ((Transform)_frame).SetSiblingIndex(_original.transform.GetSiblingIndex()); _frame.anchorMin = Vector2.zero; _frame.anchorMax = Vector2.one; _frame.offsetMin = Vector2.zero; _frame.offsetMax = new Vector2(0f, 50f); TMP_Text obj = Object.Instantiate<TMP_Text>(((Component)((Transform)partner).Find("container_name")).GetComponent<TMP_Text>(), (Transform)(object)_frame, false); obj.text = Plugin.Localization.YourInventoryText; ((Graphic)obj).raycastTarget = false; obj.alignment = (TextAlignmentOptions)514; RectTransform rectTransform = obj.rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.sizeDelta = new Vector2(0f, 50f); rectTransform.anchoredPosition = Vector2.zero; ((Component)_frame).gameObject.SetActive(false); } public void Show() { _wasActive = _original.activeSelf; _original.SetActive(false); ((Component)_frame).gameObject.SetActive(true); } public void Hide() { if (Object.op_Implicit((Object)(object)_original)) { _original.SetActive(_wasActive); } if (Object.op_Implicit((Object)(object)_frame)) { ((Component)_frame).gameObject.SetActive(false); } } public void Destroy() { if (Object.op_Implicit((Object)(object)_frame)) { Object.Destroy((Object)(object)((Component)_frame).gameObject); } } } internal sealed class PreviewTradeWindow : TradeWindow { private GameObject? _clone; private TMP_Text? _weight; private TMP_Text? _title; private float _horizontalPadding; private Inventory? _reservations; public void Initialize(string name, string title, ConfigEntry<Vector2> offset) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) RectTransform container = InventoryGui.instance.m_container; _clone = Object.Instantiate<GameObject>(((Component)container).gameObject, InventoryGui.instance.m_inventoryRoot, false); ((Object)_clone).name = name; Initialize(_clone.GetComponent<RectTransform>(), title, left: false, offset); Rect rect = base.Panel.rect; float width = ((Rect)(ref rect)).width; rect = ((Component)base.Grid).GetComponent<RectTransform>().rect; _horizontalPadding = Mathf.Max(0f, width - ((Rect)(ref rect)).width); base.Grid.m_onSelected = null; base.Grid.m_onRightClick = null; base.Grid.m_onReleased = null; base.Grid.m_onEnter = null; base.Grid.OnMoveToUpperInventoryGrid = null; base.Grid.OnMoveToLowerInventoryGrid = null; base.Grid.OnSetTouchSelection = null; base.Grid.CanDropDragOntoItem = (ItemData _) => false; foreach (Transform item in (Transform)base.Grid.m_gridRoot) { Object.Destroy((Object)(object)((Component)item).gameObject); } base.Grid.m_elements.Clear(); base.Grid.m_width = 0; base.Grid.m_height = 0; _title = ((Component)_clone.transform.Find("container_name")).GetComponent<TMP_Text>(); _title.richText = false; _title.text = title; Button[] componentsInChildren = _clone.GetComponentsInChildren<Button>(true); for (int num = 0; num < componentsInChildren.Length; num++) { ((Component)componentsInChildren[num]).gameObject.SetActive(false); } _weight = ((Component)_clone.transform.Find("Weight")).GetComponentInChildren<TMP_Text>(true); base.Group.ResetActiveElement(); base.Group.SetActive(false); Hide(); } public void SetTitle(string title) { if ((Object)(object)_title != (Object)null) { _title.text = title; } } public void Display(Inventory inventory, string title, Inventory? reservations = null) { base.Inventory = inventory; _reservations = reservations; if ((Object)(object)_title != (Object)null) { _title.text = title; } base.Panel.SetSizeWithCurrentAnchors((Axis)0, _horizontalPadding + (float)inventory.GetWidth() * base.Grid.m_elementSpace); Refresh(); } protected override void Update() { base.Update(); if (base.Visible) { Refresh(); } } public override void Show() { base.Show(); if ((Object)(object)_clone != (Object)null) { _clone.SetActive(true); } } public override void Hide() { _reservations = null; base.Hide(); if ((Object)(object)_clone != (Object)null) { _clone.SetActive(false); } } public override void Reset() { _reservations = null; base.Inventory.RemoveAll(); base.Reset(); Refresh(); } public override void Refresh() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) base.Refresh(); foreach (InventoryElement element in base.Grid.m_elements) { ItemData itemAt = base.Inventory.GetItemAt(element.Position.x, element.Position.y); ((Behaviour)element.m_equiped).enabled = itemAt?.m_equipped ?? false; ((Graphic)element.m_equiped).color = new Color(0.35f, 0.65f, 0.59f, 0.65f); } if (_reservations != null) { ReservedInventoryRenderer.Draw(base.Grid, base.Inventory, _reservations); } if ((Object)(object)_weight != (Object)null) { _weight.text = Mathf.CeilToInt(base.Inventory.GetTotalWeight()).ToString(); } } private void OnDestroy() { if ((Object)(object)_clone != (Object)null) { Object.Destroy((Object)(object)_clone); } } } internal static class ReservedInventoryRenderer { public static void Draw(InventoryGrid grid, Inventory availableInventory, Inventory reservations) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) foreach (ItemData allItem in reservations.GetAllItems()) { Vector2i gridPos = allItem.m_gridPos; InventoryElement element = grid.GetElement(gridPos.x, gridPos.y, availableInventory.GetWidth()); if ((Object)(object)element == (Object)null) { continue; } int stack = allItem.m_stack; ItemData itemAt = availableInventory.GetItemAt(gridPos.x, gridPos.y); if (itemAt == null) { ((Behaviour)element.m_icon).enabled = true; element.m_icon.sprite = allItem.GetIcon(); ((Graphic)element.m_icon).color = Color.grey; ((Behaviour)element.m_quality).enabled = allItem.m_shared.m_maxQuality > 1; element.m_quality.text = allItem.m_quality.ToString(); ((Behaviour)element.m_amount).enabled = allItem.m_shared.m_maxStackSize > 1; element.m_amount.text = stack + "/" + allItem.m_shared.m_maxStackSize; if ((Object)(object)grid.GetHoveredElement() == (Object)(object)element) { allItem.m_stack = stack; grid.CreateItemTooltip(allItem, element.m_tooltip); } } else { ((Behaviour)element.m_amount).enabled = true; element.m_amount.text = itemAt.m_stack + "/" + itemAt.m_shared.m_maxStackSize; } if ((Object)(object)grid.GetHoveredElement() == (Object)(object)element) { UITooltip tooltip = element.m_tooltip; tooltip.m_text = tooltip.m_text + "\n" + string.Format(Plugin.Localization.ReservedForTradeText, stack); } } } } internal sealed class TradeButton : MonoBehaviour { private GameObject? _object; private Button? _button; private RectTransform? _rect; private TMP_Text? _text; private Image? _image; private Color _original; private ConfigEntry<Vector2>? _offset; private bool _editing; private bool _moving; public void Initialize(string title, UnityAction action, ConfigEntry<Vector2> offset, UIGroupHandler group, string joyButton, string joyHint) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) _offset = offset; _object = Object.Instantiate<GameObject>(((Component)InventoryGui.instance.m_takeAllButton).gameObject, InventoryGui.instance.m_inventoryRoot, false); ((Object)_object).name = "PlayerTradingReforged" + joyHint; _button = _object.GetComponent<Button>(); _rect = _object.GetComponent<RectTransform>(); _text = ((Component)_button).GetComponentInChildren<TMP_Text>(true); _image = _object.GetComponent<Image>(); _original = ((Graphic)_image).color; UIGamePad component = _object.GetComponent<UIGamePad>(); component.m_zinputKey = joyButton; component.m_group = group; ((Component)_object.transform.Find("gamepad_hint")).GetComponentInChildren<TMP_Text>(true).text = joyHint; SetText(title); SetAction(action); SetActive(active: false); } private void Update() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_object == (Object)null || !_object.activeInHierarchy || (Object)(object)_rect == (Object)null || _offset == null || !_editing) { return; } Vector2 val = Vector2.op_Implicit(((Transform)_rect).InverseTransformPoint(Input.mousePosition)); if (Input.GetMouseButtonDown(0)) { Rect rect = _rect.rect; if (((Rect)(ref rect)).Contains(val)) { _moving = true; } } if (Input.GetMouseButtonUp(0)) { _moving = false; } if (_moving) { ConfigEntry<Vector2>? offset = _offset; offset.Value += new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * 8f; } } public void Place(RectTransform root, Vector2 position, float scale) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_rect == (Object)null) && _offset != null) { _rect.pivot = new Vector2(0.5f, 1f); ((Transform)_rect).localScale = Vector3.one * scale; ((Transform)_rect).position = ((Transform)root).TransformPoint(Vector2.op_Implicit(position + _offset.Value)); } } public void SetText(string text) { if ((Object)(object)_text != (Object)null) { _text.text = text; } } public void SetAction(UnityAction action) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (!((Object)(object)_button == (Object)null)) { _button.onClick = new ButtonClickedEvent(); ((UnityEvent)_button.onClick).AddListener(action); } } public void SetActive(bool active) { if ((Object)(object)_object != (Object)null) { _object.SetActive(active); } } public void SetEditMode(bool editing) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) _editing = editing; _moving = false; if ((Object)(object)_button != (Object)null) { ((Selectable)_button).interactable = !editing; } if ((Object)(object)_image != (Object)null) { ((Graphic)_image).color = (editing ? Color.Lerp(_original, Color.magenta, 0.35f) : _original); } } private void OnDestroy() { if ((Object)(object)_object != (Object)null) { Object.Destroy((Object)(object)_object); } } } internal sealed class TradeWeightBadge { private readonly RectTransform _original; private readonly RectTransform _badge; private readonly TMP_Text _text; private readonly Color _color; private bool _originalActive; public TradeWeightBadge(RectTransform original) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) _original = original; _badge = Object.Instantiate<RectTransform>(original, ((Transform)original).parent, false); ((Object)_badge).name = "PlayerTradingReforgedWeight"; _badge.pivot = new Vector2(0f, 0.5f); _badge.SetSizeWithCurrentAnchors((Axis)0, 125f); _badge.SetSizeWithCurrentAnchors((Axis)1, 96f); _text = ((Component)_badge).GetComponentInChildren<TMP_Text>(true); _color = ((Graphic)_text).color; _text.enableAutoSizing = false; _text.fontSize = 16f; _text.alignment = (TextAlignmentOptions)514; RectTransform rectTransform = _text.rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(4f, 4f); rectTransform.offsetMax = new Vector2(-4f, -8f); ((Component)_badge).gameObject.SetActive(false); } public void Show() { _originalActive = ((Component)_original).gameObject.activeSelf; ((Component)_original).gameObject.SetActive(false); ((Component)_badge).gameObject.SetActive(true); } public void Hide() { if (Object.op_Implicit((Object)(object)_original)) { ((Component)_original).gameObject.SetActive(_originalActive); } if (Object.op_Implicit((Object)(object)_badge)) { ((Component)_badge).gameObject.SetActive(false); } } public void Display(float? current, float? projected, float? capacity) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) RectTransform badge = _badge; RectTransform badge2 = _badge; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(1f, 0f); badge2.anchorMax = val; badge.anchorMin = val; _badge.anchoredPosition = new Vector2(0f, 65f); string text = Format(capacity); string text2 = Format(projected) + "/" + text; if (projected.HasValue && capacity.HasValue && projected.Value > capacity.Value) { text2 = "<color=#FF5555>" + text2 + "</color>"; } ((Graphic)_text).color = _color; _text.text = Format(current) + "/" + text + "\n<size=12>" + Plugin.Localization.ProjectedWeightText + "</size>\n" + text2; } private static string Format(float? value) { if (!value.HasValue) { return "?"; } return Mathf.CeilToInt(value.Value).ToString(); } public void Destroy() { if (Object.op_Implicit((Object)(object)_badge)) { Object.Destroy((Object)(object)((Component)_badge).gameObject); } } } internal abstract class TradeWindow : MonoBehaviour { private RectTransform? _panel; private Image? _background; private InventoryGrid? _grid; private ConfigEntry<Vector2>? _offset; private Color _originalColor; private Vector2 _anchorMin; private Vector2 _anchorMax; private Vector2 _position; private Vector2 _pivot; private Vector2 _size; private Vector3 _scale; private bool _editing; private bool _moving; private bool _accepted; protected bool Visible { get; private set; } protected bool IsLeft { get; private set; } public RectTransform Panel => _panel ?? throw new InvalidOperationException("Trade window has not initialized."); protected InventoryGrid Grid => _grid ?? throw new InvalidOperationException("Trade grid has not initialized."); public Inventory Inventory { get; protected set; } = TradeInventory.Create(); protected Vector2 UserOffset => _offset?.Value ?? Vector2.zero; public UIGroupHandler Group => ((Component)Panel).GetComponent<UIGroupHandler>(); protected void Initialize(RectTransform panel, string title, bool left, ConfigEntry<Vector2> offset) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown _panel = panel; _background = ((Component)((Transform)panel).Find("Bkg")).GetComponent<Image>(); _grid = ((Component)panel).GetComponentInChildren<InventoryGrid>(true); _offset = offset; _originalColor = ((Graphic)_background).color; _pivot = panel.pivot; _size = panel.sizeDelta; _scale = ((Transform)panel).localScale; _anchorMin = panel.anchorMin; _anchorMax = panel.anchorMax; _position = panel.anchoredPosition; IsLeft = left; Inventory = new Inventory(title, (Sprite)null, 6, 4); } protected virtual void Update() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (!Visible || (Object)(object)_panel == (Object)null || _offset == null || !_editing) { return; } Vector2 val = Vector2.op_Implicit(((Transform)_panel).InverseTransformPoint(Input.mousePosition)); if (Input.GetMouseButtonDown(0)) { Rect rect = _panel.rect; if (((Rect)(ref rect)).Contains(val)) { _moving = true; } } if (Input.GetMouseButtonUp(0)) { _moving = false; } if (_moving) { ConfigEntry<Vector2>? offset = _offset; offset.Value += new Vector2(Input.GetAxis("Mouse X") * 8f, (0f - Input.GetAxis("Mouse Y")) * 8f); } } public void Place(RectTransform root, Vector2 position, float scale) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) Panel.pivot = new Vector2(0f, 1f); ((Transform)Panel).localScale = Vector3.one * scale; ((Transform)Panel).position = ((Transform)root).TransformPoint(Vector2.op_Implicit(position + new Vector2(UserOffset.x, 0f - UserOffset.y))); }