using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using Bifrost.Cooked;
using DooDesch;
using HarmonyLib;
using InventoryExpansion;
using InventoryExpansion.Config;
using InventoryExpansion.Patches;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Mimic;
using Mimic.Actors;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "InventoryExpansion", "1.5.1", "DooDesch", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+22647a04014be1fce044a4b382452bf0748afc9b")]
[assembly: AssemblyProduct("InventoryExpansion")]
[assembly: AssemblyTitle("InventoryExpansion")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DooDesch
{
internal sealed class HostSettings
{
[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyCreated")]
private static class CreatedPatch
{
private static void Postfix(LobbyCreated_t callback)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
if ((int)callback.m_eResult != 1)
{
return;
}
foreach (HostSettings instance in Instances)
{
instance.Begin(new CSteamID(callback.m_ulSteamIDLobby), host: true);
}
}
}
[HarmonyPatch(typeof(SteamInviteDispatcher), "OnLobbyEntered")]
private static class EnteredPatch
{
private static void Prefix(LobbyEnter_t callback)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: 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)
if (callback.m_EChatRoomEnterResponse != 1)
{
return;
}
CSteamID val = default(CSteamID);
((CSteamID)(ref val))..ctor(callback.m_ulSteamIDLobby);
foreach (HostSettings instance in Instances)
{
if (!instance.isHost || !(instance.lobby == val))
{
instance.Begin(val, host: false);
}
}
}
}
[HarmonyPatch(typeof(SteamMatchmaking), "LeaveLobby")]
private static class LeavePatch
{
private static void Prefix(CSteamID steamIDLobby)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
foreach (HostSettings instance in Instances)
{
if (instance.lobby == steamIDLobby)
{
instance.Reset();
}
}
}
}
[HarmonyPatch(typeof(MainMenu), "Start")]
private static class MenuPatch
{
private static void Prefix()
{
foreach (HostSettings instance in Instances)
{
instance.Reset();
}
}
}
private static readonly List<HostSettings> Instances = new List<HostSettings>();
private static Callback<LobbyDataUpdate_t> dataCallback;
private static float nextPoll;
private readonly string modId;
private readonly string lobbyKey;
private readonly int schemaVersion;
private readonly List<ISetting> settings = new List<ISetting>();
private readonly string modernKey;
private string modernPayload;
private string lastModernPayload;
private long modernRevision;
private bool legacyHost;
private Dictionary<string, object> snapshot;
private CSteamID lobby;
private CSteamID owner;
private bool isHost;
private bool hostCreated;
private bool authorityValid;
private bool ownerChanged;
private bool published;
private string hostPayload;
private string warningReason;
private int warningRepeats;
internal bool IsInSession => lobby != CSteamID.Nil;
internal bool IsHost
{
get
{
if (isHost)
{
return authorityValid;
}
return false;
}
}
internal bool UsingLegacyHost
{
get
{
if (!isHost && authorityValid && legacyHost)
{
return modernRevision == 0;
}
return false;
}
}
internal event Action EffectiveValuesChanged;
internal event Action LiveValuesChanged;
internal HostSettings(string modId, int schemaVersion = 1)
{
if (string.IsNullOrWhiteSpace(modId))
{
throw new ArgumentException("A mod ID is required.", "modId");
}
if (schemaVersion < 1)
{
throw new ArgumentOutOfRangeException("schemaVersion");
}
this.modId = modId;
this.schemaVersion = schemaVersion;
lobbyKey = "doodesch." + modId.ToLowerInvariant() + ".settings.v" + schemaVersion;
modernKey = lobbyKey + ".extended";
if (Encoding.UTF8.GetByteCount(modernKey) >= 255)
{
throw new ArgumentException("The mod ID exceeds the Steam lobby key limit.", "modId");
}
foreach (HostSettings instance in Instances)
{
if (instance.lobbyKey == lobbyKey)
{
throw new ArgumentException("The mod already has a settings snapshot.", "modId");
}
}
Instances.Add(this);
}
internal HostSetting<T> Register<T>(MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate = null, bool live = false, bool legacy = true, Func<T> localValue = null)
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
if (entry == null)
{
throw new ArgumentNullException("entry");
}
if (string.IsNullOrWhiteSpace(((MelonPreferences_Entry)entry).Identifier))
{
throw new ArgumentException("A setting identifier is required.", "entry");
}
if (((MelonPreferences_Entry)entry).Identifier.IndexOfAny(new char[3] { '\n', '\r', '\t' }) >= 0)
{
throw new ArgumentException("Setting identifiers cannot contain wire delimiters.", "entry");
}
if (lobby != CSteamID.Nil)
{
throw new InvalidOperationException("Register settings before entering a lobby.");
}
if (typeof(T) != typeof(bool) && typeof(T) != typeof(int) && typeof(T) != typeof(float) && typeof(T) != typeof(string))
{
throw new NotSupportedException("Host settings support bool, int, float and string values.");
}
if (legacy && typeof(T) == typeof(string))
{
throw new ArgumentException("String settings must use the extended snapshot.", "legacy");
}
foreach (ISetting setting in settings)
{
if (setting.Key == ((MelonPreferences_Entry)entry).Identifier)
{
throw new ArgumentException("Duplicate setting: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
}
}
HostSetting<T> hostSetting = new HostSetting<T>(this, entry, vanillaValue, validate, live, legacy, localValue);
if (!hostSetting.IsValid(vanillaValue))
{
throw new ArgumentException("The vanilla value is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "vanillaValue");
}
if (!hostSetting.IsValid(entry.DefaultValue))
{
throw new ArgumentException("The configured default is invalid: " + ((MelonPreferences_Entry)entry).Identifier, "entry");
}
settings.Add(hostSetting);
return hostSetting;
}
internal T GetValue<T>(string key, T localValue, T vanillaValue, bool live)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
if (lobby == CSteamID.Nil)
{
return localValue;
}
if (live && !authorityValid)
{
return vanillaValue;
}
if (snapshot == null || !snapshot.TryGetValue(key, out var value))
{
return vanillaValue;
}
return (T)value;
}
internal static void Update()
{
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
if (Time.realtimeSinceStartup < nextPoll)
{
return;
}
nextPoll = Time.realtimeSinceStartup + 1f;
foreach (HostSettings instance in Instances)
{
if (instance.lobby != CSteamID.Nil)
{
instance.Refresh();
}
}
}
private void Begin(CSteamID newLobby, bool host)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: 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_003e: 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_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: 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_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
if (settings.Count == 0 || (lobby == newLobby && (!host || hostCreated)))
{
return;
}
if (lobby != newLobby)
{
Reset();
}
lobby = newLobby;
isHost = false;
hostCreated = host;
authorityValid = false;
ownerChanged = false;
snapshot = null;
published = false;
hostPayload = null;
owner = CSteamID.Nil;
try
{
owner = SteamMatchmaking.GetLobbyOwner(lobby);
CSteamID steamID = SteamUser.GetSteamID();
if (owner == CSteamID.Nil || (host ? (owner != steamID) : (owner == steamID)))
{
owner = CSteamID.Nil;
Warn("Lobby owner does not identify the expected host; using vanilla settings.");
Changed();
return;
}
isHost = host;
authorityValid = true;
if (host)
{
CaptureHostSnapshot();
}
else
{
CaptureClientSnapshot();
}
}
catch (Exception ex)
{
Warn("Initial lobby settings failed; vanilla settings are frozen for this session: " + ex.GetType().Name + ": " + ex.Message);
}
Refresh();
Changed();
}
private static void EnsureCallback()
{
if (dataCallback == null)
{
dataCallback = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdated);
}
}
private static void OnLobbyDataUpdated(LobbyDataUpdate_t callback)
{
//IL_0020: 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_002e: 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)
foreach (HostSettings instance in Instances)
{
if (instance.lobby.m_SteamID == callback.m_ulSteamIDLobby && callback.m_ulSteamIDMember == callback.m_ulSteamIDLobby)
{
if (callback.m_bSuccess == 0)
{
instance.Warn("Steam lobby data update failed; frozen session settings are retained.");
}
else
{
instance.Refresh();
}
}
}
}
private void CaptureHostSnapshot()
{
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
StringBuilder stringBuilder = new StringBuilder(schemaVersion.ToString(CultureInfo.InvariantCulture));
for (int i = 0; i < settings.Count; i++)
{
ISetting setting = settings[i];
object localValue = setting.LocalValue;
dictionary.Add(setting.Key, localValue);
if (setting.Legacy)
{
stringBuilder.Append('\n').Append(setting.Key).Append('\t')
.Append(Format(localValue));
}
}
string s = stringBuilder.ToString();
if (Encoding.UTF8.GetByteCount(s) + 1 > 8192)
{
throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
}
hostPayload = s;
snapshot = dictionary;
modernRevision = 1L;
modernPayload = BuildModern(dictionary, modernRevision);
}
private unsafe void CaptureClientSnapshot()
{
//IL_0001: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
if (!string.IsNullOrEmpty(lobbyData))
{
snapshot = ParseModern(lobbyData, out modernRevision);
lastModernPayload = lobbyData;
MelonLogger.Msg("[" + modId + "] Host settings captured; live values follow the host, lobby values remain fixed.");
return;
}
string lobbyData2 = SteamMatchmaking.GetLobbyData(lobby, lobbyKey);
if (string.IsNullOrEmpty(lobbyData2))
{
Warn("Host snapshot is absent; vanilla settings are frozen for this session.");
return;
}
snapshot = ParseSnapshot(lobbyData2);
legacyHost = true;
string[] obj = new string[5] { "[", modId, "] Host settings snapshot frozen for lobby ", null, null };
CSteamID val = lobby;
obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
obj[4] = ".";
MelonLogger.Msg(string.Concat(obj));
}
private void Refresh()
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: 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)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: 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_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
bool flag = false;
try
{
EnsureCallback();
CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(lobby);
if (ownerChanged || owner == CSteamID.Nil || lobbyOwner != owner)
{
authorityValid = false;
if (lobbyOwner != CSteamID.Nil && lobbyOwner != owner)
{
ownerChanged = true;
}
Warn("Lobby owner changed or is unavailable; authority revoked and frozen session settings retained.");
return;
}
bool num = !authorityValid;
authorityValid = true;
flag = true;
if (num)
{
MelonLogger.Msg("[" + modId + "] Pinned lobby authority recovered; frozen settings retained.");
}
if (isHost)
{
if (hostPayload != null)
{
if (published)
{
RefreshHostLive();
}
else
{
Publish();
}
}
}
else
{
RefreshClientLive();
}
}
catch (Exception ex)
{
authorityValid = flag;
Warn("Refresh lobby settings failed; frozen session settings retained: " + ex.GetType().Name + ": " + ex.Message);
}
}
private unsafe void Publish()
{
//IL_0001: 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_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: 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)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload && !SteamMatchmaking.SetLobbyData(lobby, lobbyKey, hostPayload))
{
CSteamID val = lobby;
Warn("SetLobbyData rejected the settings snapshot for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
}
else if (SteamMatchmaking.GetLobbyData(lobby, lobbyKey) != hostPayload)
{
CSteamID val = lobby;
Warn("Settings snapshot readback differs for lobby " + ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
}
else if (PublishModern(modernPayload))
{
published = true;
Recover();
string[] obj = new string[5] { "[", modId, "] Settings snapshot confirmed by local lobby readback for ", null, null };
CSteamID val = lobby;
obj[3] = ((object)(*(CSteamID*)(&val))/*cast due to .constrained prefix*/).ToString();
obj[4] = ".";
MelonLogger.Msg(string.Concat(obj));
}
}
private Dictionary<string, object> ParseSnapshot(string payload)
{
if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
{
throw new FormatException("Settings snapshot exceeds the Steam metadata limit.");
}
string[] array = payload.Split('\n');
if (array[0] != schemaVersion.ToString(CultureInfo.InvariantCulture))
{
throw new FormatException("Settings snapshot schema mismatch.");
}
if (array.Length != settings.FindAll((ISetting s) => s.Legacy).Count + 1)
{
throw new FormatException("Settings snapshot has missing or extra keys.");
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
for (int num = 1; num < array.Length; num++)
{
string text = array[num];
int num2 = text.IndexOf('\t');
if (num2 <= 0 || num2 != text.LastIndexOf('\t') || text.IndexOf('\r') >= 0)
{
throw new FormatException("Settings snapshot has an invalid entry delimiter.");
}
string key = text.Substring(0, num2);
if (dictionary.ContainsKey(key))
{
throw new FormatException("Settings snapshot has a duplicate or missing key.");
}
ISetting setting = settings.Find((ISetting candidate) => candidate.Key == key && candidate.Legacy);
if (setting == null)
{
throw new FormatException("Settings snapshot has an unknown key: " + key);
}
dictionary.Add(key, setting.Parse(text.Substring(num2 + 1)));
}
return dictionary;
}
private string BuildModern(Dictionary<string, object> values, long revision)
{
StringBuilder stringBuilder = new StringBuilder("2\t" + revision.ToString(CultureInfo.InvariantCulture));
foreach (ISetting setting in settings)
{
stringBuilder.Append('\n').Append(setting.Key).Append('\t')
.Append(Format(values[setting.Key]));
}
string text = stringBuilder.ToString();
if (Encoding.UTF8.GetByteCount(text) + 1 > 8192)
{
throw new FormatException("Extended settings exceed the Steam metadata limit.");
}
return text;
}
private Dictionary<string, object> ParseModern(string payload, out long revision)
{
if (Encoding.UTF8.GetByteCount(payload) + 1 > 8192)
{
throw new FormatException("Extended settings exceed the Steam metadata limit.");
}
string[] array = payload.Split('\n');
string[] array2 = array[0].Split('\t');
if (array2.Length != 2 || array2[0] != "2" || !long.TryParse(array2[1], NumberStyles.None, CultureInfo.InvariantCulture, out revision) || revision < 1)
{
throw new FormatException("Invalid extended settings schema or revision.");
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
for (int i = 1; i < array.Length; i++)
{
int num = array[i].IndexOf('\t');
if (num <= 0 || num != array[i].LastIndexOf('\t') || array[i].IndexOf('\r') >= 0)
{
throw new FormatException("Invalid extended settings delimiter.");
}
string key = array[i].Substring(0, num);
if (!hashSet.Add(key))
{
throw new FormatException("Duplicate extended setting: " + key);
}
ISetting setting = settings.Find((ISetting s) => s.Key == key);
if (setting != null)
{
dictionary.Add(key, setting.Parse(array[i].Substring(num + 1)));
}
}
return dictionary;
}
private bool PublishModern(string payload)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload && !SteamMatchmaking.SetLobbyData(lobby, modernKey, payload))
{
Warn("SetLobbyData rejected the extended settings; last confirmed values retained.");
return false;
}
if (SteamMatchmaking.GetLobbyData(lobby, modernKey) != payload)
{
Warn("Extended settings readback differs; last confirmed values retained.");
return false;
}
return true;
}
private void RefreshHostLive()
{
if (snapshot == null)
{
return;
}
Dictionary<string, object> dictionary = new Dictionary<string, object>(snapshot, StringComparer.Ordinal);
bool flag = false;
foreach (ISetting setting in settings)
{
if (setting.Live)
{
object localValue = setting.LocalValue;
if (!dictionary.TryGetValue(setting.Key, out var value) || !object.Equals(value, localValue))
{
dictionary[setting.Key] = localValue;
flag = true;
}
}
}
if (!flag)
{
if (PublishModern(modernPayload))
{
Recover();
}
return;
}
long revision = checked(modernRevision + 1);
string payload = BuildModern(dictionary, revision);
if (PublishModern(payload))
{
snapshot = dictionary;
modernRevision = revision;
modernPayload = payload;
Recover();
Changed(this.LiveValuesChanged);
}
}
private void RefreshClientLive()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
string lobbyData = SteamMatchmaking.GetLobbyData(lobby, modernKey);
if (string.IsNullOrEmpty(lobbyData) || lobbyData == lastModernPayload)
{
return;
}
long revision;
Dictionary<string, object> dictionary = ParseModern(lobbyData, out revision);
if (revision <= modernRevision)
{
Warn("Extended settings revision did not advance; last confirmed values retained.");
return;
}
Dictionary<string, object> dictionary2 = ((snapshot == null) ? new Dictionary<string, object>(StringComparer.Ordinal) : new Dictionary<string, object>(snapshot, StringComparer.Ordinal));
bool flag = false;
foreach (ISetting setting in settings)
{
if (setting.Live && dictionary.TryGetValue(setting.Key, out var value) && (!dictionary2.TryGetValue(setting.Key, out var value2) || !object.Equals(value2, value)))
{
dictionary2[setting.Key] = value;
flag = true;
}
}
snapshot = dictionary2;
modernRevision = revision;
lastModernPayload = lobbyData;
Recover();
if (flag)
{
Changed(this.LiveValuesChanged);
}
}
private static string Format(object value)
{
if (value is string s)
{
return "s:" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s));
}
if (value is float num)
{
return num.ToString("R", CultureInfo.InvariantCulture);
}
if (value is bool)
{
if (!(bool)value)
{
return "false";
}
return "true";
}
return ((int)value).ToString(CultureInfo.InvariantCulture);
}
private void Warn(string reason)
{
if (warningReason == reason)
{
warningRepeats++;
return;
}
if (warningRepeats > 0)
{
MelonLogger.Warning("[" + modId + "] Previous settings warning repeated " + warningRepeats + " times.");
}
warningReason = reason;
warningRepeats = 0;
MelonLogger.Warning("[" + modId + "] " + reason);
}
private void Recover()
{
if (warningReason != null)
{
MelonLogger.Msg("[" + modId + "] Lobby settings recovered after " + (warningRepeats + 1) + " warning occurrences.");
warningReason = null;
warningRepeats = 0;
}
}
private void Changed()
{
Changed(this.EffectiveValuesChanged);
}
private void Changed(Action handlers)
{
if (handlers == null)
{
return;
}
Delegate[] invocationList = handlers.GetInvocationList();
for (int i = 0; i < invocationList.Length; i++)
{
Action action = (Action)invocationList[i];
try
{
action();
}
catch (Exception ex)
{
MelonLogger.Error("[" + modId + "] Apply effective settings failed.", ex);
}
}
}
private void Reset()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
bool num = lobby != CSteamID.Nil;
lobby = CSteamID.Nil;
owner = CSteamID.Nil;
isHost = false;
hostCreated = false;
authorityValid = false;
ownerChanged = false;
published = false;
snapshot = null;
hostPayload = null;
modernPayload = null;
lastModernPayload = null;
modernRevision = 0L;
legacyHost = false;
warningReason = null;
warningRepeats = 0;
if (num)
{
MelonLogger.Msg("[" + modId + "] Session settings cleared; local settings restored.");
Changed();
}
}
}
internal interface ISetting
{
string Key { get; }
object LocalValue { get; }
bool Live { get; }
bool Legacy { get; }
bool IsValid(object value);
object Parse(string value);
}
internal sealed class HostSetting<T> : ISetting
{
private readonly HostSettings owner;
private readonly MelonPreferences_Entry<T> entry;
private readonly T vanillaValue;
private readonly Func<T, bool> validate;
private readonly Func<T> localValue;
private bool invalidLocal;
public bool Live { get; }
public bool Legacy { get; }
internal T Value => owner.GetValue(Key, (T)LocalValue, vanillaValue, Live);
public string Key => ((MelonPreferences_Entry)entry).Identifier;
public object LocalValue
{
get
{
T val = ((localValue == null) ? entry.Value : localValue());
if (IsValid(val))
{
if (invalidLocal)
{
MelonLogger.Msg("Local setting recovered: " + Key);
}
invalidLocal = false;
return val;
}
if (!invalidLocal)
{
MelonLogger.Warning("Invalid local setting " + Key + "; using its configured default without overwriting the file.");
}
invalidLocal = true;
return entry.DefaultValue;
}
}
internal HostSetting(HostSettings owner, MelonPreferences_Entry<T> entry, T vanillaValue, Func<T, bool> validate, bool live, bool legacy, Func<T> localValue)
{
this.owner = owner;
this.entry = entry;
this.vanillaValue = vanillaValue;
this.validate = validate;
this.localValue = localValue;
Live = live;
Legacy = legacy;
}
public bool IsValid(object value)
{
if (!(value is T arg))
{
return false;
}
if (value is float f && (float.IsNaN(f) || float.IsInfinity(f)))
{
return false;
}
if (value is string text && (Encoding.UTF8.GetByteCount(text) > 1024 || text.IndexOf('\0') >= 0))
{
return false;
}
if (validate != null)
{
return validate(arg);
}
return true;
}
public object Parse(string value)
{
object obj;
int result2;
float result3;
if (typeof(T) == typeof(bool) && bool.TryParse(value, out var result))
{
obj = result;
}
else if (typeof(T) == typeof(int) && int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
{
obj = result2;
}
else if (typeof(T) == typeof(float) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result3))
{
obj = result3;
}
else
{
if (!(typeof(T) == typeof(string)) || !value.StartsWith("s:", StringComparison.Ordinal))
{
throw new FormatException("Invalid settings value: " + Key);
}
obj = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(value.Substring(2)));
}
if (!IsValid(obj))
{
throw new FormatException("Settings value is out of range: " + Key);
}
return obj;
}
}
internal static class ModVersion
{
internal const string Current = "1.5.1";
}
}
namespace InventoryExpansion
{
public sealed class Core : MelonMod
{
public override void OnInitializeMelon()
{
InventoryExpansionPreferences.Initialize();
InventoryExpansionPreferences.EffectiveValuesChanged += ApplyEffectiveSettings;
ApplyEffectiveSettings();
MelonLogger.Msg("InventoryExpansion initialized. Enabled={0}", new object[1] { InventoryExpansionPreferences.Enabled });
}
private static void ApplyEffectiveSettings()
{
BackpackPanelPatch.ResetForSettingsChange();
BackpackMovementSpeedPatch.ResetLocalState();
}
public override void OnUpdate()
{
HostSettings.Update();
}
}
}
namespace InventoryExpansion.Patches
{
internal static class BackpackLayout
{
internal static int[] Rows(int slots)
{
if (slots < 0)
{
throw new ArgumentOutOfRangeException("slots");
}
if (slots == 0)
{
return Array.Empty<int>();
}
int num = (slots + 7) / 8;
int[] array = new int[num];
for (int i = 0; i < num; i++)
{
array[i] = slots / num + ((i < slots % num) ? 1 : 0);
}
return array;
}
}
[HarmonyPatch]
internal static class BackpackPanelPatch
{
private static readonly FieldInfo MainInventoryField = AccessTools.Field(typeof(GameMainBase), "inventoryui");
private static UIPrefab_Inventory _inventoryUi;
private static RectTransform _nativeHud;
private static Vector2 _hudRest;
private static RectTransform _backpackPanel;
private static RectTransform _firstNativeSlot;
private static RectTransform _lastNativeSlot;
private static GameObject _bodyVisuals;
private static UIPrefab_KeyIcon _keyBadge;
private static readonly List<GameObject> Containers = new List<GameObject>();
private static bool _slotsMoved;
private static bool _backpackFullyVisible;
private static bool _targetOpen;
private static bool _layoutPending;
private static float _offset;
private static float _openOffset;
private static float _nextBindAttempt;
private static float _nextBadgeAttempt;
private static bool _badgeMissing;
private static int _savedStandardSlot;
private static object _animationCoroutine;
private static string _lastLayoutError;
private static bool _waitingForSlotLayout;
private static FieldInfo _uimanField;
internal static bool IsBackpackFullyVisible => _backpackFullyVisible;
internal static bool IsBackpackOpen => _targetOpen;
[HarmonyPostfix]
[HarmonyPatch(typeof(UIPrefab_Inventory), "Awake")]
private static void AwakePostfix(UIPrefab_Inventory __instance)
{
BackpackSceneChangeHandler.Initialize();
Relayout(__instance);
}
private static UIPrefab_Inventory LocalUi()
{
if (!((Object)(object)Hub.Main == (Object)null))
{
object? value = MainInventoryField.GetValue(Hub.Main);
return (UIPrefab_Inventory)((value is UIPrefab_Inventory) ? value : null);
}
return null;
}
internal static void Relayout(UIPrefab_Inventory ui)
{
if (!_layoutPending && InventoryExpansionPreferences.Enabled)
{
_layoutPending = true;
MelonCoroutines.Start(LayoutAfterFrame(ui));
}
}
private static IEnumerator LayoutAfterFrame(UIPrefab_Inventory ui)
{
yield return null;
_layoutPending = false;
if ((Object)(object)ui == (Object)null || (Object)(object)LocalUi() != (Object)(object)ui || !InventoryExpansionPreferences.Enabled)
{
yield break;
}
try
{
if ((Object)(object)_inventoryUi != (Object)(object)ui || (Object)(object)_backpackPanel == (Object)null)
{
Bind(ui);
}
if (ArrangeSlots())
{
if (_lastLayoutError != null)
{
MelonLogger.Msg("[InventoryExpansion] Native backpack layout recovered.");
}
_lastLayoutError = null;
}
}
catch (Exception ex)
{
string text = ex.GetType().Name + ": " + ex.Message;
if (_lastLayoutError != text)
{
MelonLogger.Error("[InventoryExpansion] Native backpack layout failed: " + ex);
}
_lastLayoutError = text;
}
}
private static void Bind(UIPrefab_Inventory ui)
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Expected O, but got Unknown
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Expected O, but got Unknown
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Expected O, but got Unknown
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: Unknown result type (might be due to invalid IL or missing references)
//IL_0148: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_0153: Unknown result type (might be due to invalid IL or missing references)
//IL_015d: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0164: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
HideBackpackCompletely();
if ((Object)(object)_backpackPanel != (Object)null)
{
Object.Destroy((Object)(object)((Component)_backpackPanel).gameObject);
}
Containers.Clear();
_firstNativeSlot = (_lastNativeSlot = null);
_keyBadge = null;
_inventoryUi = ui;
Transform uE_rootNode = ui.UE_rootNode;
_nativeHud = (RectTransform)(object)((uE_rootNode is RectTransform) ? uE_rootNode : null);
if ((Object)(object)_nativeHud == (Object)null)
{
throw new InvalidOperationException("Native inventory root is missing.");
}
_hudRest = _nativeHud.anchoredPosition;
_backpackPanel = (RectTransform)new GameObject("InventoryExpansion_BackpackPanel", new Type[1] { typeof(RectTransform) }).transform;
((Transform)_backpackPanel).SetParent(((Transform)_nativeHud).parent, false);
RectTransform val = (RectTransform)((Transform)_nativeHud).parent;
RectTransform backpackPanel = _backpackPanel;
Vector2 anchorMin = (_backpackPanel.anchorMax = val.pivot);
backpackPanel.anchorMin = anchorMin;
_backpackPanel.pivot = new Vector2(0.5f, 0f);
_bodyVisuals = new GameObject("InventoryExpansion_Group", new Type[1] { typeof(RectTransform) });
_bodyVisuals.transform.SetParent((Transform)(object)_backpackPanel, false);
RectTransform val2 = (RectTransform)_bodyVisuals.transform;
val2.anchorMin = Vector2.zero;
val2.anchorMax = Vector2.one;
anchorMin = (val2.offsetMax = Vector2.zero);
val2.offsetMin = anchorMin;
_slotsMoved = false;
}
private static bool ArrangeSlots()
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Expected O, but got Unknown
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Expected O, but got Unknown
//IL_0080: 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_0092: 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_0154: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_0173: Expected O, but got Unknown
//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
//IL_0221: Unknown result type (might be due to invalid IL or missing references)
//IL_0232: Unknown result type (might be due to invalid IL or missing references)
//IL_0237: Unknown result type (might be due to invalid IL or missing references)
//IL_046f: Unknown result type (might be due to invalid IL or missing references)
//IL_0474: Unknown result type (might be due to invalid IL or missing references)
//IL_048b: Unknown result type (might be due to invalid IL or missing references)
//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
//IL_04a8: Unknown result type (might be due to invalid IL or missing references)
//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
//IL_02da: Expected O, but got Unknown
//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0306: Expected O, but got Unknown
//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
//IL_02ad: Expected O, but got Unknown
//IL_031b: Unknown result type (might be due to invalid IL or missing references)
//IL_0322: Unknown result type (might be due to invalid IL or missing references)
//IL_0335: Unknown result type (might be due to invalid IL or missing references)
//IL_0345: Unknown result type (might be due to invalid IL or missing references)
//IL_0385: Unknown result type (might be due to invalid IL or missing references)
//IL_039b: Unknown result type (might be due to invalid IL or missing references)
//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
//IL_03c4: Unknown result type (might be due to invalid IL or missing references)
//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
//IL_03d5: Unknown result type (might be due to invalid IL or missing references)
//IL_03da: Unknown result type (might be due to invalid IL or missing references)
//IL_03e4: Unknown result type (might be due to invalid IL or missing references)
//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
//IL_050a: Unknown result type (might be due to invalid IL or missing references)
//IL_052f: Unknown result type (might be due to invalid IL or missing references)
//IL_0534: Unknown result type (might be due to invalid IL or missing references)
List<Slot> list = (List<Slot>)InventoryUiPatches.SlotsField.GetValue(_inventoryUi);
int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
int num2 = Math.Min(list.Count, InventoryUiPatches.LogicalSlotCount) - num;
if (num2 <= 0)
{
return true;
}
int[] array = BackpackLayout.Rows(num2);
RectTransform val = (_firstNativeSlot = (RectTransform)list[0].root.transform);
_lastNativeSlot = (RectTransform)list[3].root.transform;
Rect rect = val.rect;
float width = ((Rect)(ref rect)).width;
rect = val.rect;
float height = ((Rect)(ref rect)).height;
if (width <= 0f || height <= 0f)
{
if (!_waitingForSlotLayout)
{
MelonLogger.Msg("[InventoryExpansion] Waiting for the native inventory slot layout.");
}
_waitingForSlotLayout = true;
return false;
}
if (_waitingForSlotLayout)
{
MelonLogger.Msg("[InventoryExpansion] Native inventory slot layout is ready.");
}
_waitingForSlotLayout = false;
HorizontalLayoutGroup component = ((Component)((Transform)val).parent).GetComponent<HorizontalLayoutGroup>();
float num3 = ((component != null) ? ((HorizontalOrVerticalLayoutGroup)component).spacing : 15f);
float num4 = 8f;
float num5 = (float)array.Max() * width + (float)(array.Max() - 1) * num3;
float num6 = (float)array.Length * height + (float)(array.Length - 1) * num4;
_backpackPanel.sizeDelta = new Vector2(num5 + 24f, num6 + 36f + 24f);
RectTransform parent = (RectTransform)((Transform)_backpackPanel).parent;
Vector3[] array2 = (Vector3[])(object)new Vector3[4];
val.GetWorldCorners(array2);
float num7 = array2.Min((Vector3 point) => ((Transform)parent).InverseTransformPoint(point).y) - _offset;
float x = ((Transform)parent).InverseTransformPoint(((Transform)val).position).x;
float x2 = ((Transform)parent).InverseTransformPoint(list[3].root.transform.position).x;
rect = parent.rect;
float num8 = Mathf.Max(((Rect)(ref rect)).yMin + 12f, num7 - 36f - 12f);
_backpackPanel.anchoredPosition = new Vector2((x + x2) * 0.5f, num8);
rect = _backpackPanel.rect;
_openOffset = num8 + ((Rect)(ref rect)).height + 12f - num7;
int num9 = num;
Vector2 val5 = default(Vector2);
Vector2 val7 = default(Vector2);
for (int num10 = 0; num10 < array.Length; num10++)
{
float num11 = (float)array[num10] * width + (float)(array[num10] - 1) * num3;
int num12 = 0;
while (num12 < array[num10])
{
Slot val2 = list[num9];
RectTransform val3;
if (num9 - num < Containers.Count)
{
val3 = (RectTransform)Containers[num9 - num].transform;
}
else
{
GameObject val4 = new GameObject("InventoryExpansion_SlotContainer_" + num9, new Type[1] { typeof(RectTransform) });
val4.transform.SetParent((Transform)(object)_backpackPanel, false);
Containers.Add(val4);
val3 = (RectTransform)val4.transform;
}
RectTransform obj = val3;
RectTransform obj2 = val3;
((Vector2)(ref val5))..ctor(0.5f, 1f);
obj2.anchorMax = val5;
obj.anchorMin = val5;
val3.pivot = new Vector2(0.5f, 0.5f);
val3.sizeDelta = new Vector2(width, height);
val3.anchoredPosition = new Vector2((0f - num11) / 2f + width / 2f + (float)num12 * (width + num3), -12f - height / 2f - (float)num10 * (height + num4));
RectTransform val6 = (RectTransform)val2.root.transform;
((Transform)val6).SetParent((Transform)(object)val3, false);
((Vector2)(ref val7))..ctor(0.5f, 0.5f);
val6.pivot = val7;
val5 = (val6.anchorMin = (val6.anchorMax = val7));
val6.sizeDelta = new Vector2(width, height);
val6.anchoredPosition = Vector2.zero;
((Transform)val6).localScale = Vector3.one;
val2.group = null;
num12++;
num9++;
}
}
for (int num13 = _bodyVisuals.transform.childCount - 1; num13 >= 0; num13--)
{
Object.Destroy((Object)(object)((Component)_bodyVisuals.transform.GetChild(num13)).gameObject);
}
Color color = ((Graphic)_inventoryUi.UE_BG_normal1).color;
RectImage("Backing", new Vector2(num5 + 20f, num6 + 12f), new Vector2(0f, 48f + num6 / 2f), new Color(color.r * 0.2f, color.g * 0.15f, color.b * 0.1f, 0.07f));
int[] array3 = new int[2] { -1, 1 };
foreach (int num15 in array3)
{
RectImage("End", new Vector2(2f, 26f), new Vector2((float)num15 * (num5 / 2f + 8f), 48f + num6 / 2f), color);
}
_bodyVisuals.transform.SetAsFirstSibling();
EnsureBadge();
_slotsMoved = true;
ApplyOffset(_targetOpen ? _openOffset : 0f);
ShowContents(_targetOpen);
return true;
}
internal static void FollowNativeHud()
{
//IL_0049: 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_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: 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)
if (!((Object)(object)_backpackPanel == (Object)null) && ((Component)_backpackPanel).gameObject.activeInHierarchy && !((Object)(object)_firstNativeSlot == (Object)null) && !((Object)(object)_lastNativeSlot == (Object)null))
{
Transform parent = ((Transform)_backpackPanel).parent;
float x = parent.InverseTransformPoint(((Transform)_firstNativeSlot).position).x;
float x2 = parent.InverseTransformPoint(((Transform)_lastNativeSlot).position).x;
_backpackPanel.anchoredPosition = new Vector2((x + x2) / 2f, _backpackPanel.anchoredPosition.y);
}
}
private static Image RectImage(string name, Vector2 size, Vector2 position, Color color)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: 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_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: 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_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("InventoryExpansion_" + name, new Type[2]
{
typeof(RectTransform),
typeof(Image)
});
val.transform.SetParent(_bodyVisuals.transform, false);
RectTransform val2 = (RectTransform)val.transform;
Vector2 val3 = default(Vector2);
((Vector2)(ref val3))..ctor(0.5f, 0f);
val2.anchorMax = val3;
val2.anchorMin = val3;
val2.sizeDelta = size;
val2.anchoredPosition = position;
Image component = val.GetComponent<Image>();
((Graphic)component).color = color;
((Graphic)component).raycastTarget = false;
return component;
}
private static void EnsureBadge()
{
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
//IL_0123: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_keyBadge == (Object)null)
{
if (Time.unscaledTime < _nextBadgeAttempt)
{
return;
}
_nextBadgeAttempt = Time.unscaledTime + 1f;
UIPrefab_KeyIcon val = ((IEnumerable<UIPrefab_KeyIcon>)Object.FindObjectsByType<UIPrefab_KeyIcon>((FindObjectsInactive)1, (FindObjectsSortMode)0)).FirstOrDefault((Func<UIPrefab_KeyIcon, bool>)((UIPrefab_KeyIcon icon) => !((Object)icon).name.StartsWith("InventoryExpansion_")));
if ((Object)(object)val == (Object)null)
{
if (!_badgeMissing)
{
MelonLogger.Warning("[InventoryExpansion] Waiting for the native key-cap prefab.");
}
_badgeMissing = true;
return;
}
if (_badgeMissing)
{
MelonLogger.Msg("[InventoryExpansion] Native key-cap prefab is available.");
}
_badgeMissing = false;
_keyBadge = Object.Instantiate<UIPrefab_KeyIcon>(val, (Transform)(object)_backpackPanel, false);
((Object)_keyBadge).name = "InventoryExpansion_KeyHint";
RectTransform val2 = (RectTransform)((Component)_keyBadge).transform;
Vector2 val3 = default(Vector2);
((Vector2)(ref val3))..ctor(0.5f, 0f);
val2.anchorMax = val3;
val2.anchorMin = val3;
val2.pivot = new Vector2(0.5f, 0f);
val2.anchoredPosition = Vector2.zero;
val2.sizeDelta = new Vector2(38f, 36f);
((Transform)val2).localScale = Vector3.one;
}
string text = ((object)InventoryExpansionPreferences.BackpackKey/*cast due to .constrained prefix*/).ToString();
_keyBadge.SetKey("k_" + text.ToLowerInvariant());
((Component)_keyBadge.UE_Key).gameObject.SetActive(true);
_keyBadge.UE_UI_KeyText.text = text;
((Component)_keyBadge).gameObject.SetActive(true);
}
private static void ApplyOffset(float offset)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
_offset = offset;
if ((Object)(object)_nativeHud != (Object)null)
{
_nativeHud.anchoredPosition = _hudRest + new Vector2(0f, offset);
}
}
private static void ShowContents(bool visible)
{
if ((Object)(object)_bodyVisuals != (Object)null)
{
_bodyVisuals.SetActive(visible);
}
int num = Math.Max(0, InventoryUiPatches.LogicalSlotCount - InvenSlotLayout.FirstExtensionSlotKey + 1);
for (int i = 0; i < Containers.Count; i++)
{
Containers[i].SetActive(visible && i < num);
}
}
internal static void EnsurePeekIfHidden()
{
if (!InventoryExpansionPreferences.Enabled)
{
return;
}
UIPrefab_Inventory val = LocalUi();
if ((Object)(object)val == (Object)null)
{
return;
}
if ((Object)(object)_inventoryUi != (Object)(object)val || !_slotsMoved || (Object)(object)_backpackPanel == (Object)null)
{
if (Time.unscaledTime >= _nextBindAttempt)
{
_nextBindAttempt = Time.unscaledTime + 1f;
Relayout(val);
}
}
else
{
((Component)_backpackPanel).gameObject.SetActive(true);
EnsureBadge();
}
}
internal static void ToggleBackpack()
{
if (!((Object)(object)_backpackPanel == (Object)null) && !IsLoadingScreenActive() && !IsGamePaused())
{
_targetOpen = !_targetOpen;
if (_animationCoroutine != null)
{
MelonCoroutines.Stop(_animationCoroutine);
}
((Component)_backpackPanel).gameObject.SetActive(true);
ShowContents(_targetOpen);
if (InventoryExpansionPreferences.AnimationSeconds == 0f)
{
ApplyOffset(_targetOpen ? _openOffset : 0f);
_backpackFullyVisible = _targetOpen;
_animationCoroutine = null;
}
else
{
_animationCoroutine = MelonCoroutines.Start(Animate(_targetOpen));
}
}
}
private static IEnumerator Animate(bool open)
{
float start = _offset;
float target = (open ? _openOffset : 0f);
float elapsed = 0f;
_backpackFullyVisible = false;
float duration = InventoryExpansionPreferences.AnimationSeconds;
while (elapsed < duration && (Object)(object)_nativeHud != (Object)null)
{
elapsed += Time.unscaledDeltaTime;
float num = Mathf.Clamp01(elapsed / duration);
ApplyOffset(Mathf.Lerp(start, target, 1f - Mathf.Pow(1f - num, 3f)));
yield return null;
}
ApplyOffset(target);
_backpackFullyVisible = open;
_animationCoroutine = null;
}
internal static void HideBackpackCompletely()
{
if (_targetOpen || _backpackFullyVisible)
{
GameMainBase main = Hub.Main;
HandleCursorHandoff((main != null) ? main.GetMyAvatar() : null, opening: false);
}
if (_animationCoroutine != null)
{
MelonCoroutines.Stop(_animationCoroutine);
}
_animationCoroutine = null;
_targetOpen = (_backpackFullyVisible = false);
ApplyOffset(0f);
if ((Object)(object)_backpackPanel != (Object)null)
{
((Component)_backpackPanel).gameObject.SetActive(false);
}
}
internal static void ResetForSettingsChange()
{
HideBackpackCompletely();
_savedStandardSlot = 0;
}
internal static void HandleCursorHandoff(ProtoActor avatar, bool opening)
{
if ((Object)(object)avatar == (Object)null || (opening ? (!InventoryExpansionPreferences.SelectBackpackSlotOnOpen) : (!InventoryExpansionPreferences.RestoreStandardSlotOnClose)))
{
return;
}
object obj = InventorySelectionHelper.GetActorInventoryField()?.GetValue(avatar);
if (obj != null)
{
int selectedInventorySlotIndex = avatar.GetSelectedInventorySlotIndex();
int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
if (opening && selectedInventorySlotIndex < num)
{
_savedStandardSlot = selectedInventorySlotIndex;
}
int num2 = (opening ? num : (avatar.GetVisibleInventorySlotIndices().Contains(_savedStandardSlot) ? _savedStandardSlot : 0));
if (!opening || InventoryUiPatches.LogicalSlotCount > num)
{
InventorySelectionHelper.GetSelectSlotMethod(obj.GetType()).Invoke(obj, new object[1] { num2 });
}
}
}
internal static bool IsGamePaused()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
try
{
if (Time.timeScale <= 0.01f)
{
return true;
}
if ((int)Cursor.lockState == 0 && Cursor.visible && IsInGame())
{
return true;
}
return false;
}
catch
{
return false;
}
}
internal static bool IsInGame()
{
try
{
GameMainBase main = Hub.Main;
ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
if ((Object)(object)val == (Object)null)
{
return false;
}
return val.AmIAvatar();
}
catch (Exception arg)
{
MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] IsInGame check failed: {arg}");
return false;
}
}
private static bool IsInLoadingScreen()
{
try
{
if ((Object)(object)Hub.s == (Object)null)
{
return true;
}
GameMainBase main = Hub.Main;
ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
if ((Object)(object)val == (Object)null)
{
return true;
}
return !val.AmIAvatar();
}
catch
{
return false;
}
}
internal static bool IsLoadingScreenActive()
{
try
{
Hub s = Hub.s;
if ((Object)(object)s == (Object)null)
{
return false;
}
if (_uimanField == null)
{
_uimanField = typeof(Hub).GetField("<uiman>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
}
object? obj = _uimanField?.GetValue(s);
UIPrefab_Scene_Loading val = ((UIManager)(((obj is UIManager) ? obj : null)?)).ui_sceneloading;
return (Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled;
}
catch
{
return false;
}
}
}
[HarmonyPatch(typeof(ProtoActor), "Update")]
internal static class BackpackInputUpdatePatch
{
private static bool wasKeyPressedLastFrame;
[HarmonyPostfix]
private unsafe static void Update_Postfix(ProtoActor __instance)
{
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
try
{
if (!InventoryExpansionPreferences.Enabled || !__instance.AmIAvatar())
{
return;
}
if (BackpackPanelPatch.IsLoadingScreenActive())
{
BackpackPanelPatch.HideBackpackCompletely();
wasKeyPressedLastFrame = false;
return;
}
if (BackpackPanelPatch.IsGamePaused())
{
BackpackPanelPatch.HideBackpackCompletely();
wasKeyPressedLastFrame = false;
return;
}
BackpackPanelPatch.EnsurePeekIfHidden();
Keyboard current = Keyboard.current;
if (current == null)
{
wasKeyPressedLastFrame = false;
return;
}
KeyCode backpackKey = InventoryExpansionPreferences.BackpackKey;
Key val = (Key)0;
try
{
val = (Key)Enum.Parse(typeof(Key), ((object)(*(KeyCode*)(&backpackKey))/*cast due to .constrained prefix*/).ToString());
}
catch
{
MelonLogger.Warning($"[InventoryExpansion][BackpackPanel] Could not convert KeyCode {backpackKey} to Input System Key");
return;
}
bool isPressed = ((ButtonControl)current[val]).isPressed;
bool num = isPressed && !wasKeyPressedLastFrame;
wasKeyPressedLastFrame = isPressed;
if (num)
{
bool flag = !BackpackPanelPatch.IsBackpackOpen;
if (flag && InventoryExpansionPreferences.BlockLargeItemsInBackpack && LargeItemBackpackPatch.IsLargeHeldItem(__instance))
{
LargeItemBackpackPatch.ShowBackpackBlockedMessage("You can't open the backpack while holding a large item.");
return;
}
BackpackPanelPatch.ToggleBackpack();
BackpackPanelPatch.HandleCursorHandoff(__instance, flag);
}
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Update postfix failed: {arg}");
}
}
}
[HarmonyPatch(typeof(ProtoActor))]
internal static class BackpackMovementSpeedPatch
{
private static FieldInfo _netSyncActorDataField;
private static bool _fieldInitialized;
private static long _originalMoveSpeedWalk;
private static long _originalMoveSpeedRun;
private static bool _speedReduced;
private static ProtoActor _speedReducedActor;
internal static void ResetLocalState()
{
try
{
RestoreMoveSpeed(_speedReducedActor);
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][Movement] Failed to restore local movement speed on settings change: {arg}");
}
}
internal static void RestoreForActor(ProtoActor actor)
{
if (actor == _speedReducedActor)
{
ResetLocalState();
}
}
[HarmonyPostfix]
[HarmonyPatch("Update")]
private static void Update_Postfix(ProtoActor __instance)
{
try
{
if (!__instance.AmIAvatar())
{
return;
}
if (!InventoryExpansionPreferences.Enabled)
{
RestoreMoveSpeed(__instance);
return;
}
if (_speedReduced && _speedReducedActor != __instance)
{
ResetLocalState();
if (_speedReduced)
{
return;
}
}
if (!_fieldInitialized)
{
_netSyncActorDataField = typeof(ProtoActor).GetField("netSyncActorData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
_fieldInitialized = true;
}
if (_netSyncActorDataField == null)
{
return;
}
object value = _netSyncActorDataField.GetValue(__instance);
if (value == null)
{
return;
}
FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
if (field == null || field2 == null)
{
return;
}
if (BackpackPanelPatch.IsBackpackFullyVisible && InventoryExpansionPreferences.ReduceMovementSpeed)
{
if (!_speedReduced)
{
_originalMoveSpeedWalk = (long)(field.GetValue(value) ?? ((object)350L));
_originalMoveSpeedRun = (long)(field2.GetValue(value) ?? ((object)700L));
_speedReduced = true;
_speedReducedActor = __instance;
}
field.SetValue(value, (long)((float)_originalMoveSpeedWalk * InventoryExpansionPreferences.MovementSpeedMultiplier));
field2.SetValue(value, (long)((float)_originalMoveSpeedRun * InventoryExpansionPreferences.MovementSpeedMultiplier));
}
else
{
RestoreMoveSpeed(value, field, field2);
}
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][Movement] Movement speed patch failed: {arg}");
}
}
private static void RestoreMoveSpeed(ProtoActor instance)
{
if (!_speedReduced || _netSyncActorDataField == null || instance != _speedReducedActor)
{
return;
}
object value = _netSyncActorDataField.GetValue(instance);
if (value != null)
{
FieldInfo field = value.GetType().GetField("MoveSpeedWalk", BindingFlags.Instance | BindingFlags.Public);
FieldInfo field2 = value.GetType().GetField("MoveSpeedRun", BindingFlags.Instance | BindingFlags.Public);
if (field != null && field2 != null)
{
RestoreMoveSpeed(value, field, field2);
}
}
}
private static void RestoreMoveSpeed(object netSyncActorData, FieldInfo moveSpeedWalkField, FieldInfo moveSpeedRunField)
{
if (_speedReduced)
{
moveSpeedWalkField.SetValue(netSyncActorData, _originalMoveSpeedWalk);
moveSpeedRunField.SetValue(netSyncActorData, _originalMoveSpeedRun);
_speedReduced = false;
_speedReducedActor = null;
_originalMoveSpeedWalk = 0L;
_originalMoveSpeedRun = 0L;
}
}
}
[HarmonyPatch(typeof(ProtoActor), "OnDestroy")]
internal static class BackpackProtoActorDestroyPatch
{
[HarmonyPostfix]
private static void OnDestroy_Postfix(ProtoActor __instance)
{
try
{
BackpackMovementSpeedPatch.RestoreForActor(__instance);
if (__instance.AmIAvatar())
{
BackpackSceneChangeHandler.HideBackpack();
}
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][BackpackPanel] ProtoActor OnDestroy patch failed: {arg}");
}
}
}
internal static class BackpackSceneChangeHandler
{
[CompilerGenerated]
private static class <>O
{
public static UnityAction<Scene, Scene> <0>__OnActiveSceneChanged;
public static WillRenderCanvases <1>__FollowNativeHud;
}
private static bool _initialized;
internal static void Initialize()
{
//IL_0038: 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_0043: Expected O, but got Unknown
if (!_initialized)
{
SceneManager.activeSceneChanged += OnActiveSceneChanged;
object obj = <>O.<1>__FollowNativeHud;
if (obj == null)
{
WillRenderCanvases val = BackpackPanelPatch.FollowNativeHud;
<>O.<1>__FollowNativeHud = val;
obj = (object)val;
}
Canvas.willRenderCanvases += (WillRenderCanvases)obj;
_initialized = true;
}
}
private static void OnActiveSceneChanged(Scene previousScene, Scene newScene)
{
try
{
if (!IsInGame())
{
HideBackpack();
}
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][BackpackPanel] Scene change handler failed: {arg}");
}
}
private static bool IsInGame()
{
try
{
GameMainBase main = Hub.Main;
ProtoActor val = ((main != null) ? main.GetMyAvatar() : null);
if ((Object)(object)val == (Object)null)
{
return false;
}
return val.AmIAvatar();
}
catch
{
return false;
}
}
internal static void HideBackpack()
{
BackpackPanelPatch.HideBackpackCompletely();
BackpackMovementSpeedPatch.ResetLocalState();
}
}
[HarmonyPatch]
internal static class InventoryControllerPatches
{
private static IEnumerable<MethodBase> TargetMethods()
{
yield return AccessTools.Method(typeof(InventoryController), "Reset", (Type[])null, (Type[])null);
yield return AccessTools.Method(typeof(InventoryController), "RecalculateSlotCount", (Type[])null, (Type[])null);
}
private static int GetBasicSlotCount(InventoryController inventory)
{
return 4 + ((inventory.HasDedicatedSlots && InventoryExpansionPreferences.Enabled) ? InventoryExpansionPreferences.AdditionalSlots : 0);
}
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
MethodInfo methodInfo = AccessTools.Method(typeof(InventoryController), "GetDedicatedSlotCount", (Type[])null, (Type[])null);
for (int i = 0; i + 3 < list.Count; i++)
{
if (CodeInstructionExtensions.LoadsConstant(list[i], 4L) && !(list[i + 1].opcode != OpCodes.Ldarg_0) && CodeInstructionExtensions.Calls(list[i + 2], methodInfo) && !(list[i + 3].opcode != OpCodes.Add))
{
list[i].opcode = OpCodes.Ldarg_0;
list[i].operand = null;
list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(InventoryControllerPatches), "GetBasicSlotCount", (Type[])null, (Type[])null)));
return list;
}
}
throw new InvalidOperationException("[InventoryExpansion] Native base inventory capacity calculation was not found.");
}
}
[HarmonyPatch(typeof(InventoryController), "FindSlotForAdd")]
internal static class InventoryPickupPatches
{
private static readonly FieldInfo SelfField = AccessTools.Field(typeof(InventoryController), "_self");
private static readonly FieldInfo SlotsField = AccessTools.Field(typeof(InventoryController), "_inventorySlots");
private static void Postfix(InventoryController __instance, ref int __result)
{
if (!InventoryExpansionPreferences.Enabled || !InventoryExpansionPreferences.FillBackpackFirst || !BackpackPanelPatch.IsBackpackFullyVisible || __result <= 0 || __result > 4)
{
return;
}
object? value = SelfField.GetValue(__instance);
VActor val = (VActor)((value is VActor) ? value : null);
GameMainBase main = Hub.Main;
ProtoActor val2 = ((main != null) ? main.GetMyAvatar() : null);
if (val == null || (Object)(object)val2 == (Object)null || val.ObjectID != val2.ActorID)
{
return;
}
Dictionary<int, ItemElement> obj = (Dictionary<int, ItemElement>)SlotsField.GetValue(__instance);
int num = 0;
foreach (KeyValuePair<int, ItemElement> item in obj)
{
if (item.Key >= InvenSlotLayout.FirstExtensionSlotKey && item.Value == null && !__instance.IsSlotLocked(item.Key) && (num == 0 || item.Key < num))
{
num = item.Key;
}
}
if (num != 0)
{
__result = num;
}
}
}
internal static class InventorySelectionHelper
{
internal static Type GetInventoryType()
{
return typeof(ProtoActor).GetNestedType("Inventory", BindingFlags.Public | BindingFlags.NonPublic);
}
internal static FieldInfo GetSelectedSlotIndexField(Type inventoryType)
{
return inventoryType?.GetField("selectedSlotIndex", BindingFlags.Instance | BindingFlags.NonPublic);
}
internal static FieldInfo GetSlotSizeField(Type inventoryType)
{
return inventoryType?.GetField("slotSize", BindingFlags.Instance | BindingFlags.NonPublic);
}
internal static FieldInfo GetActorInventoryField()
{
return typeof(ProtoActor).GetField("inventory", BindingFlags.Instance | BindingFlags.NonPublic);
}
internal static MethodInfo GetSelectSlotMethod(Type inventoryType)
{
return inventoryType?.GetMethod("SelectSlot", BindingFlags.Instance | BindingFlags.NonPublic);
}
}
[HarmonyPatch]
internal static class InventorySelectionNextPatches
{
private static MethodBase TargetMethod()
{
return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectNextSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
[HarmonyPrefix]
private static bool SelectNextSlot_Prefix(object __instance)
{
return InventorySelectionCommon.HandleSlotSelection(__instance, forward: true);
}
}
[HarmonyPatch]
internal static class InventorySelectionPreviousPatches
{
private static MethodBase TargetMethod()
{
return InventorySelectionHelper.GetInventoryType()?.GetMethod("SelectPreviousSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
[HarmonyPrefix]
private static bool SelectPreviousSlot_Prefix(object __instance)
{
return InventorySelectionCommon.HandleSlotSelection(__instance, forward: false);
}
}
internal static class InventorySelectionCommon
{
internal static bool HandleSlotSelection(object __instance, bool forward)
{
try
{
if (!InventoryExpansionPreferences.Enabled)
{
return true;
}
if (__instance == null)
{
return true;
}
Type type = __instance.GetType();
FieldInfo selectedSlotIndexField = InventorySelectionHelper.GetSelectedSlotIndexField(type);
FieldInfo slotSizeField = InventorySelectionHelper.GetSlotSizeField(type);
if (selectedSlotIndexField == null || slotSizeField == null)
{
return true;
}
int item = (int)selectedSlotIndexField.GetValue(__instance);
bool open = BackpackPanelPatch.IsBackpackFullyVisible;
List<int> list = ((List<int>)type.GetMethod("GetVisibleSlotIndices").Invoke(__instance, null)).FindAll((int num2) => num2 >= InvenSlotLayout.FirstExtensionSlotKey - 1 == open);
if (list.Count == 0)
{
return false;
}
int num = list.IndexOf(item);
int index = ((num >= 0) ? ((num + (forward ? 1 : (-1)) + list.Count) % list.Count) : ((!forward) ? (list.Count - 1) : 0));
InventorySelectionHelper.GetSelectSlotMethod(type).Invoke(__instance, new object[1] { list[index] });
return false;
}
catch (Exception arg)
{
MelonLogger.Error($"[InventoryExpansion][Selection] Slot selection prefix failed: {arg}");
return true;
}
}
}
[HarmonyPatch(typeof(UIPrefab_Inventory))]
internal static class InventoryUiPatches
{
private static UIPrefab_Inventory _currentUi;
internal static readonly FieldInfo SlotsField = AccessTools.Field(typeof(UIPrefab_Inventory), "inventorySlots");
internal static int LogicalSlotCount { get; private set; }
[HarmonyPostfix]
[HarmonyPatch("Awake")]
private static void AwakePostfix(UIPrefab_Inventory __instance)
{
EnsureSlots(__instance, 0);
}
[HarmonyPrefix]
[HarmonyPatch("UpdateSlot")]
private static void UpdatePrefix(UIPrefab_Inventory __instance, List<InventoryItem> inventoryItems)
{
EnsureSlots(__instance, inventoryItems.Count);
}
private static void EnsureSlots(UIPrefab_Inventory ui, int count)
{
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: 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_0156: Unknown result type (might be due to invalid IL or missing references)
//IL_0185: Unknown result type (might be due to invalid IL or missing references)
//IL_01b9: Expected O, but got Unknown
if (InventoryExpansionPreferences.Enabled)
{
List<Slot> list = (List<Slot>)SlotsField.GetValue(ui);
int num = InvenSlotLayout.FirstExtensionSlotKey - 1;
int num2 = Math.Max(count, num + InventoryExpansionPreferences.AdditionalSlots);
bool flag = (Object)(object)_currentUi != (Object)(object)ui || LogicalSlotCount != num2 || list.Count < num2;
_currentUi = ui;
LogicalSlotCount = num2;
Slot val = list[num] ?? throw new InvalidOperationException("[InventoryExpansion] Native extension slot template is missing.");
while (list.Count < num2)
{
GameObject val2 = Object.Instantiate<GameObject>(val.root, val.root.transform.parent, false);
((Object)val2).name = "InventoryExpansion_Slot_" + list.Count;
val2.SetActive(false);
list.Add(new Slot
{
root = val2,
group = val.group,
itemGroup = ((Component)CloneNode(val.itemGroup.transform, val.root.transform, val2.transform)).gameObject,
frame = CloneNode(val.frame, val.root.transform, val2.transform),
waitEvent = CloneNode(val.waitEvent, val.root.transform, val2.transform),
image = ((Component)CloneNode(((Component)val.image).transform, val.root.transform, val2.transform)).GetComponent<Image>(),
stackCount = ((Component)CloneNode(val.stackCount.transform, val.root.transform, val2.transform)).GetComponent<TMP_Text>()
});
}
if (flag)
{
BackpackPanelPatch.Relayout(ui);
}
}
}
private static Transform CloneNode(Transform node, Transform source, Transform target)
{
if ((Object)(object)node == (Object)(object)source)
{
return target;
}
Stack<int> stack = new Stack<int>();
Transform val = node;
while ((Object)(object)val != (Object)(object)source)
{
if ((Object)(object)val == (Object)null)
{
throw new InvalidOperationException("[InventoryExpansion] Native slot reference leaves its root.");
}
stack.Push(val.GetSiblingIndex());
val = val.parent;
}
while (stack.Count > 0)
{
target = target.GetChild(stack.Pop());
}
return target;
}
}
[HarmonyPatch(typeof(LootingLevelObject))]
internal static class LargeItemBackpackPatch
{
private static PropertyInfo _tablemanProp;
private static FieldInfo _uiprefabsField;
private static bool _reflectionInitialized;
private static float _lastMessageTime;
private static void EnsureReflection()
{
if (!_reflectionInitialized)
{
_reflectionInitialized = true;
_tablemanProp = typeof(Hub).GetProperty("tableman", BindingFlags.Instance | BindingFlags.NonPublic);
}
}
[HarmonyPrefix]
[HarmonyPatch("TryInteract")]
private static bool TryInteract_Prefix(LootingLevelObject __instance, ref bool __result)
{
try
{
if (!InventoryExpansionPreferences.Enabled || !InventoryExpansionPreferences.BlockLargeItemsInBackpack)
{
return true;
}
if (!BackpackPanelPatch.IsBackpackFullyVisible)
{
return true;
}
ItemMasterInfo itemMasterInfo = Inventory.GetItemMasterInfo(__instance.itemMasterID);
if (itemMasterInfo == null || !itemMasterInfo.ForbidChange)
{
return true;
}
ShowTooLargeMessage();
__result = false;
return false;
}
catch (Exception ex)
{
MelonLogger.Warning("[InventoryExpansion][LargeItem] TryInteract prefix failed, allowing default grab: " + ex.Message);
return true;
}
}
internal static bool IsLargeHeldItem(ProtoActor actor)
{
try
{
int result;
if (actor == null)
{
result = 0;
}
else
{
InventoryItem selectedInventoryItem = actor.GetSelectedInventoryItem();
result = ((((selectedInventoryItem == null) ? ((bool?)null) : selectedInventoryItem.MasterInfo?.ForbidChange) == true) ? 1 : 0);
}
return (byte)result != 0;
}
catch
{
return false;
}
}
private static void ShowTooLargeMessage()
{
ShowBackpackBlockedMessage("This is too large for your backpack.");
}
internal static void ShowBackpackBlockedMessage(string message)
{
try
{
float unscaledTime = Time.unscaledTime;
if (unscaledTime - _lastMessageTime < 1f)
{
return;
}
_lastMessageTime = unscaledTime;
EnsureReflection();
object s = Hub.s;
object obj = ((s == null) ? null : _tablemanProp?.GetValue(s));
if (obj != null)
{
if (_uiprefabsField == null)
{
_uiprefabsField = obj.GetType().GetField("uiprefabs", BindingFlags.Instance | BindingFlags.Public);
}
object? obj2 = _uiprefabsField?.GetValue(obj);
MMUIPrefabTable val = (MMUIPrefabTable)((obj2 is MMUIPrefabTable) ? obj2 : null);
if (val != null)
{
val.ShowTimerDialog("ToastSimple", 0f, new object[1] { message });
}
}
}
catch (Exception ex)
{
MelonLogger.Warning("[InventoryExpansion][LargeItem] showing message failed: " + ex.Message);
}
}
}
}
namespace InventoryExpansion.Config
{
internal static class InventoryExpansionPreferences
{
private const string CategoryId = "InventoryExpansion";
private static MelonPreferences_Category _category;
private static MelonPreferences_Entry<bool> _enabled;
private static MelonPreferences_Entry<int> _additionalSlots;
private static MelonPreferences_Entry<string> _backpackKey;
private static MelonPreferences_Entry<bool> _reduceMovementSpeed;
private static MelonPreferences_Entry<bool> _selectBackpackSlotOnOpen;
private static MelonPreferences_Entry<bool> _restoreStandardSlotOnClose;
private static MelonPreferences_Entry<bool> _fillBackpackFirst;
private static MelonPreferences_Entry<bool> _blockLargeItemsInBackpack;
private static HostSetting<bool> _effectiveEnabled;
private static HostSetting<int> _effectiveAdditionalSlots;
private static HostSetting<bool> _effectiveReduceMovementSpeed;
private static HostSetting<bool> _effectiveFillBackpackFirst;
private static HostSetting<bool> _effectiveBlockLargeItemsInBackpack;
private static HostSetting<float> _movementSpeedMultiplier;
private static MelonPreferences_Entry<float> _animationSeconds;
internal static bool Enabled => _effectiveEnabled?.Value ?? _enabled?.Value ?? true;
internal static int AdditionalSlots => _effectiveAdditionalSlots?.Value ?? ClampToValidBackpackSize(_additionalSlots?.Value ?? 4);
internal static KeyCode BackpackKey
{
get
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
if (_backpackKey == null)
{
return (KeyCode)99;
}
string value = _backpackKey.Value;
if (string.IsNullOrEmpty(value))
{
return (KeyCode)99;
}
if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
{
return result;
}
return (KeyCode)99;
}
}
internal static bool ReduceMovementSpeed => _effectiveReduceMovementSpeed?.Value ?? _reduceMovementSpeed?.Value ?? true;
internal static float MovementSpeedMultiplier => _movementSpeedMultiplier?.Value ?? 1f;
internal static float AnimationSeconds
{
get
{
if (_animationSeconds == null || float.IsNaN(_animationSeconds.Value) || float.IsInfinity(_animationSeconds.Value))
{
return 0.25f;
}
return Mathf.Clamp(_animationSeconds.Value, 0f, 2f);
}
}
internal static bool SelectBackpackSlotOnOpen => _selectBackpackSlotOnOpen?.Value ?? true;
internal static bool RestoreStandardSlotOnClose => _restoreStandardSlotOnClose?.Value ?? true;
internal static bool FillBackpackFirst => _effectiveFillBackpackFirst?.Value ?? _fillBackpackFirst?.Value ?? true;
internal static bool BlockLargeItemsInBackpack => _effectiveBlockLargeItemsInBackpack?.Value ?? _blockLargeItemsInBackpack?.Value ?? true;
internal static event Action EffectiveValuesChanged;
internal static void Initialize()
{
if (_category == null)
{
_category = MelonPreferences.CreateCategory("InventoryExpansion", "InventoryExpansion");
_enabled = CreateEntry("Enabled", defaultValue: true, "Enabled", "Enable InventoryExpansion functionality. When disabled, the mod will not modify game behavior.");
_additionalSlots = CreateEntry("AdditionalSlots", 4, "Additional Inventory Slots", "Number of extra inventory slots to add on top of the game's default inventory size. Any whole number from 4 to 24 is supported. Rows stay balanced with at most eight slots each. Values outside this range are clamped.");
_backpackKey = CreateEntry("BackpackKey", "C", "Backpack Toggle Key", "Key to toggle backpack visibility. Press to switch between standard inventory and backpack.");
_reduceMovementSpeed = CreateEntry("ReduceMovementSpeed", defaultValue: true, "Reduce Movement Speed", "Host setting, live. Apply MovementSpeedMultiplier while the backpack is fully open.");
_selectBackpackSlotOnOpen = CreateEntry("SelectBackpackSlotOnOpen", defaultValue: true, "Select Backpack Slot On Open", "When opening the backpack, move the selected slot to the first backpack slot.");
_restoreStandardSlotOnClose = CreateEntry("RestoreStandardSlotOnClose", defaultValue: true, "Restore Standard Slot On Close", "When closing the backpack, return the selected slot to the standard inventory (the slot that was selected before opening).");
_fillBackpackFirst = CreateEntry("FillBackpackFirst", defaultValue: true, "Fill Backpack First", "While the backpack is open, picked-up items fill the backpack slots before the standard inventory. Takes effect only when you are the host or in single-player.");
_blockLargeItemsInBackpack = CreateEntry("BlockLargeItemsInBackpack", defaultValue: true, "Block Large Items In Backpack", "When enabled, large items (the ones you cannot switch away from while holding them, e.g. two-handed items) cannot be picked up into the backpack while it is open. Trying to do so shows a message instead. Disable to allow large items in the backpack.");
_additionalSlots.Value = ClampToValidBackpackSize(_additionalSlots.Value);
_animationSeconds = CreateEntry("AnimationSeconds", 0.25f, "Backpack Animation Time", "Local display setting. HUD animation duration in seconds, 0-2. Zero opens immediately. Default: 0.25");
HostSettings hostSettings = new HostSettings("InventoryExpansion");
_effectiveEnabled = hostSettings.Register(_enabled, vanillaValue: false);
_effectiveAdditionalSlots = hostSettings.Register(_additionalSlots, 0, (int value) => value == 0 || (value >= 4 && value <= 24));
_effectiveReduceMovementSpeed = hostSettings.Register(_reduceMovementSpeed, vanillaValue: false, null, live: true);
_effectiveFillBackpackFirst = hostSettings.Register(_fillBackpackFirst, vanillaValue: false, null, live: true);
_effectiveBlockLargeItemsInBackpack = hostSettings.Register(_blockLargeItemsInBackpack, vanillaValue: false, null, live: true);
_movementSpeedMultiplier = hostSettings.Register(CreateEntry("MovementSpeedMultiplier", 0.5f, "Backpack Movement Multiplier", "Host setting, live. Walking and running speed while the backpack is open, 0-1. Default: 0.5"), 1f, (float value) => value >= 0f && value <= 1f, live: true, legacy: false);
hostSettings.EffectiveValuesChanged += delegate
{
InventoryExpansionPreferences.EffectiveValuesChanged?.Invoke();
};
}
}
private static MelonPreferences_Entry<T> CreateEntry<T>(string identifier, T defaultValue, string displayName, string description = null)
{
if (_category == null)
{
throw new InvalidOperationException("Preference category not initialized.");
}
return _category.CreateEntry<T>(identifier, defaultValue, displayName, description, false, false, (ValueValidator)null, (string)null);
}
private static int ClampToValidBackpackSize(int value)
{
return Math.Clamp(value, 4, 24);
}
}
}