using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("KinkoCraft.StackableFood")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.10.0")]
[assembly: AssemblyInformationalVersion("1.0.10+662e0fe809020726d34895866fd6ad385a3c9b55")]
[assembly: AssemblyProduct("Stackable Food")]
[assembly: AssemblyTitle("KinkoCraft.StackableFood")]
[assembly: AssemblyVersion("1.0.10.0")]
[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 KinkoCraft.StackableFood
{
internal static class PluginMeta
{
public const string Guid = "KinkoCraft.StackableFood";
public const string Name = "Stackable Food";
public const string Version = "1.0.10";
public const string ItemStackerFixGuid = "com.travv.aleandtale.itemstack99safe";
public const ushort ItemStackerFixCap = 99;
}
[BepInPlugin("KinkoCraft.StackableFood", "Stackable Food", "1.0.10")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class StackableFoodPlugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
internal static ConfigEntry<int> StandaloneMaxStack;
internal static ConfigEntry<bool> IncludeDrinks;
private void Awake()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
StandaloneMaxStack = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StandaloneMaxStack", 10, new ConfigDescription("How many food items stack in one inventory slot when ItemStackerFix is NOT installed. With ItemStackerFix installed this is ignored and the cap is 99.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 99), Array.Empty<object>()));
IncludeDrinks = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeDrinks", true, "Make drinks (beer, juice, etc.) stackable too. Set to false to leave drinks unstacked.");
Harmony val = new Harmony("KinkoCraft.StackableFood");
val.PatchAll(typeof(FoodStackPatch));
val.PatchAll(typeof(ServingStackFix));
val.PatchAll(typeof(WorldSourceStackFix));
Log.LogInfo((object)"Stackable Food v1.0.10 loaded.");
}
}
public class FoodStackPatch
{
[HarmonyPatch(typeof(ItemManager), "Awake")]
[HarmonyPostfix]
private static void BumpFoodStacks(ItemManager __instance)
{
bool flag = Chainloader.PluginInfos.ContainsKey("com.travv.aleandtale.itemstack99safe");
ushort num = (ushort)(flag ? 99 : ((ushort)StackableFoodPlugin.StandaloneMaxStack.Value));
bool value = StackableFoodPlugin.IncludeDrinks.Value;
ItemData[] array = __instance.itemDataHub?.itemData;
if (array == null)
{
StackableFoodPlugin.Log.LogWarning((object)"itemDataHub.itemData was null; no food made stackable.");
return;
}
int num2 = 0;
ItemData[] array2 = array;
foreach (ItemData val in array2)
{
if (!((Object)(object)val == (Object)null) && IsFood(val, value) && val.maxStack < num && val.maxStack <= 1)
{
val.maxStack = num;
num2++;
}
}
StackableFoodPlugin.Log.LogInfo((object)(string.Format("Stackable Food: cap={0} ({1}), ", num, flag ? "ItemStackerFix detected" : "standalone") + $"includeDrinks={value}, updated {num2} item(s)."));
}
private static bool IsFood(ItemData data, bool includeDrinks)
{
//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_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
Type type = data.type;
if (type - 4 > 1)
{
if ((int)type == 6)
{
return includeDrinks;
}
return false;
}
return true;
}
}
public class ServingStackFix
{
[ThreadStatic]
private static bool _serveExecutePass;
private static readonly FieldInfo RpcStageField = AccessTools.Field(typeof(NetworkBehaviour), "__rpc_exec_stage");
[HarmonyPatch(typeof(ServingTableSlot), "ServeSingleDishServerRpc")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_ServeSingleDish(IEnumerable<CodeInstruction> instructions)
{
return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemById", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneById", (Type[])null, (Type[])null), "ServeSingleDishServerRpc");
}
[HarmonyPatch(typeof(TableFeedPlace), "ServeDish")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_TableFeedServe(IEnumerable<CodeInstruction> instructions)
{
return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemByDataId", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneByDataId", (Type[])null, (Type[])null), "TableFeedPlace.ServeDish");
}
[HarmonyPatch(typeof(HelperWaiterServeDishState), "TryServeCustomerDish")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_WaiterServe(IEnumerable<CodeInstruction> instructions)
{
return SwapCall(instructions, AccessTools.Method(typeof(ContainerNet), "RemoveItemByDataId", (Type[])null, (Type[])null), AccessTools.Method(typeof(ServingStackFix), "RemoveOneByDataId", (Type[])null, (Type[])null), "HelperWaiterServeDishState.TryServeCustomerDish");
}
private static IEnumerable<CodeInstruction> SwapCall(IEnumerable<CodeInstruction> instructions, MethodInfo from, MethodInfo to, string where)
{
int swapped = 0;
foreach (CodeInstruction instruction in instructions)
{
if ((instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Call) && instruction.operand as MethodInfo == from)
{
yield return new CodeInstruction(OpCodes.Call, (object)to);
swapped++;
}
else
{
yield return instruction;
}
}
if (swapped == 0)
{
StackableFoodPlugin.Log.LogWarning((object)("ServingStackFix: '" + from.Name + "' call not found in " + where + "; stacked dishes may still be lost there."));
}
}
[HarmonyPatch(typeof(ServingTable), "ServeDishesServerRpc")]
[HarmonyPrefix]
private static void CaptureServeStage(ServingTable __instance)
{
_serveExecutePass = IsServerExecuteStage((NetworkBehaviour)(object)__instance);
}
[HarmonyPatch(typeof(ServingTable), "ServeDishesServerRpc")]
[HarmonyPostfix]
private static void FillRemainingSlots(ServingTable __instance, ServerRpcParams serverRpcParams)
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_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_0056: 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_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: 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_0085: 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_00c7: 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)
ContainerNet val = default(ContainerNet);
if (!_serveExecutePass || !ContainerManager.Instance.GetPlayerContainer(serverRpcParams.Receive.SenderClientId, ref val))
{
return;
}
ServingTableSlot val2 = default(ServingTableSlot);
ItemData val4 = default(ItemData);
while (__instance.HaveFreeSlot(ref val2))
{
Item val3 = default(Item);
bool flag = false;
foreach (Item item in StringExtension.ToList<Item>(val.items))
{
if (item.amount > 0 && ItemManager.Instance.GetItemData((uint)item.dataId, ref val4) && IsServable(val4.type))
{
val3 = item;
flag = true;
break;
}
}
if (flag && val.RemoveAmount(val3.dataId, (ushort)1))
{
val2.itemDataId.Value = val3.dataId;
val2.itemRarity = val3.rarity;
continue;
}
break;
}
}
private static bool IsServerExecuteStage(NetworkBehaviour nb)
{
object obj = RpcStageField?.GetValue(nb);
if (obj != null)
{
return obj.ToString() == "Execute";
}
return false;
}
internal static bool IsServable(Type type)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Invalid comparison between Unknown and I4
if ((int)type != 5 && (int)type != 4)
{
return (int)type == 6;
}
return true;
}
[HarmonyPatch(typeof(ItemManager), "GetItemSellPrice")]
[HarmonyPostfix]
private static void FixFoodSellPrice(Item item, ItemData itemData, ref ushort __result)
{
//IL_000a: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Invalid comparison between Unknown and I4
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Invalid comparison between Unknown and I4
//IL_004c: 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_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Invalid comparison between Unknown and I4
//IL_0094: 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_009b: Invalid comparison between Unknown and I4
if ((Object)(object)itemData == (Object)null || !IsServable(itemData.type) || item.amount <= 1)
{
return;
}
float num = (int)itemData.price;
if (itemData.hasRarity)
{
if (itemData.shopRarityPrices != null && ((UDictionary<Rarity, ushort>)(object)itemData.shopRarityPrices).Count > 0)
{
num = (int)((UDictionary<Rarity, ushort>)(object)itemData.shopRarityPrices)[item.rarity];
}
else if ((int)item.rarity == 1)
{
num *= 1.2f;
}
else if ((int)item.rarity == 2)
{
num *= 1.6f;
}
else if ((int)item.rarity == 3)
{
num *= 2.2f;
}
else if ((int)item.rarity == 4)
{
num *= 3f;
}
}
long num2 = (long)(int)num * (long)item.amount;
__result = (ushort)((num2 > 65535) ? 65535 : num2);
}
public static bool RemoveOneById(ContainerNet cont, uint itemId)
{
//IL_0017: 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_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)cont == (Object)null)
{
return false;
}
Item val = default(Item);
if (cont.GetItemById(itemId, ref val, false) && val.amount > 1)
{
val.amount--;
cont.SetItem(val, val.order);
Game.Instance.InvokeGameEvent((GameEvent)16, (uint)val.dataId, 1);
return true;
}
return cont.RemoveItemById(itemId);
}
public static bool RemoveOneByDataId(ContainerNet cont, ushort dataId, out Item removed)
{
//IL_0001: 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_002c: 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_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: 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_0079: Unknown result type (might be due to invalid IL or missing references)
removed = default(Item);
if ((Object)(object)cont == (Object)null)
{
return false;
}
foreach (Item item in StringExtension.ToList<Item>(cont.items))
{
if (item.dataId == dataId && item.amount > 0)
{
removed = item;
removed.amount = 1;
if (item.amount > 1)
{
Item val = item;
val.amount--;
cont.SetItem(val, val.order);
Game.Instance.InvokeGameEvent((GameEvent)16, (uint)item.dataId, 1);
}
else
{
cont.RemoveItemById(item.id);
}
return true;
}
}
return false;
}
}
public class WorldSourceStackFix
{
[HarmonyPatch(typeof(CollectibleNet), "OnNetworkSpawn")]
[HarmonyPrefix]
private static void DetectFreshSource(CollectibleNet __instance, out bool __state)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
__state = ((NetworkBehaviour)__instance).IsServer && __instance.item != null && __instance.item.Value.dataId == 0;
}
[HarmonyPatch(typeof(CollectibleNet), "OnNetworkSpawn")]
[HarmonyPostfix]
private static void ClampFreshSource(CollectibleNet __instance, bool __state)
{
//IL_0017: 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_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
if (__state && !((Object)(object)__instance.itemData == (Object)null) && ServingStackFix.IsServable(__instance.itemData.type))
{
Item value = __instance.item.Value;
if (value.amount > 1)
{
value.amount = 1;
__instance.item.Value = value;
}
}
}
[HarmonyPatch(typeof(ItemCharger), "InteractServerRpc")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_BarrelDispense(IEnumerable<CodeInstruction> instructions)
{
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Expected O, but got Unknown
FieldInfo fieldInfo = AccessTools.Field(typeof(ItemData), "maxStack");
FieldInfo fieldInfo2 = AccessTools.Field(typeof(Item), "amount");
MethodInfo methodInfo = AccessTools.Method(typeof(WorldSourceStackFix), "DispenseAmount", (Type[])null, (Type[])null);
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
int num = SwapItemCtors(list);
int num2 = 0;
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldfld && list[i].operand as FieldInfo == fieldInfo && i + 1 < list.Count && list[i + 1].opcode == OpCodes.Stfld && list[i + 1].operand as FieldInfo == fieldInfo2)
{
list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
num2++;
}
}
if (num == 0 && num2 == 0)
{
StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no dispense sites found in ItemCharger.InteractServerRpc; barrels may still hand out full stacks.");
}
return list;
}
[HarmonyPatch(typeof(ItemDispenser), "TakeServerRpc")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_ItemDispenser(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
if (SwapItemCtors(list) == 0)
{
StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no Item(itemData) construction found in ItemDispenser.TakeServerRpc; barrel dispensers may still hand out full stacks.");
}
return list;
}
[HarmonyPatch(typeof(ItemCombiner), "InteractServerRpc")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Fix_ItemCombiner(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
if (SwapItemCtors(list) == 0)
{
StackableFoodPlugin.Log.LogWarning((object)"WorldSourceStackFix: no Item(itemData) construction found in ItemCombiner.InteractServerRpc; combine stations may still hand out full stacks.");
}
return list;
}
private static int SwapItemCtors(List<CodeInstruction> code)
{
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Expected O, but got Unknown
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Expected O, but got Unknown
ConstructorInfo constructorInfo = AccessTools.Constructor(typeof(Item), new Type[1] { typeof(ItemData) }, false);
MethodInfo methodInfo = AccessTools.Method(typeof(WorldSourceStackFix), "MakeOne", (Type[])null, (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(typeof(WorldSourceStackFix), "InitOne", (Type[])null, (Type[])null);
int num = 0;
for (int i = 0; i < code.Count; i++)
{
if (!(code[i].operand as ConstructorInfo != constructorInfo))
{
if (code[i].opcode == OpCodes.Newobj)
{
code[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
num++;
}
else if (code[i].opcode == OpCodes.Call)
{
code[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
num++;
}
}
}
return num;
}
public static Item MakeOne(ItemData data)
{
//IL_0026: 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)
Item result = default(Item);
((Item)(ref result))..ctor(data);
if ((Object)(object)data != (Object)null && ServingStackFix.IsServable(data.type))
{
result.amount = 1;
}
return result;
}
public static void InitOne(ref Item item, ItemData data)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: 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)
item = new Item(data);
if ((Object)(object)data != (Object)null && ServingStackFix.IsServable(data.type))
{
item.amount = 1;
}
}
public static ushort DispenseAmount(ItemData data)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)data == (Object)null)
{
return 1;
}
if (!ServingStackFix.IsServable(data.type))
{
return data.maxStack;
}
return 1;
}
}
}