using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using LazyBearTechnology;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace CrossZoneZombieSupply;
[BepInPlugin("sieme.gk2.cross-zone-zombie-supply", "Cross Zone Zombie Supply", "1.1.0")]
public sealed class Plugin : BaseUnityPlugin
{
internal static ConfigEntry<bool> Enabled;
internal static ConfigEntry<bool> GlobalConstructionStorage;
private void Awake()
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Allow caretaker zombies to supply from chests in every world zone.");
GlobalConstructionStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "GlobalConstructionStorage", true, "Allow construction outside work zones to use materials from all saved-scene chests and the player backpack.");
new Harmony("sieme.gk2.cross-zone-zombie-supply").PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Cross Zone Zombie Supply loaded");
}
}
[HarmonyPatch(typeof(WgoData), "GetCraftableMultiInventory")]
internal static class GlobalConstructionInventory
{
private static void Postfix(WgoData __instance, bool __0, ref MultiInventory __result)
{
if (!Plugin.GlobalConstructionStorage.Value || __0 || __result == null || __instance.WorldZoneData != null)
{
return;
}
HashSet<Inventory> hashSet = new HashSet<Inventory>(__result.inventoryList);
MainGame instance = MainGame.Instance;
Inventory val = (((Object)(object)instance == (Object)null || instance.GameSave == null || instance.GameSave.playerData == null) ? null : instance.GameSave.playerData.Inventory);
if (val != null && hashSet.Add(val))
{
__result.Add(val);
}
foreach (WgoData item in Supply.AllChests())
{
Inventory inventory = item.Inventory;
if (hashSet.Add(inventory))
{
__result.Add(inventory);
}
}
}
}
internal static class Supply
{
internal static IEnumerable<WgoData> AllChests()
{
MainGame game = MainGame.Instance;
if ((Object)(object)game == (Object)null || game.GameSave == null || game.GameSave.worldData == null)
{
yield break;
}
HashSet<WgoData> seen = new HashSet<WgoData>();
foreach (GameSceneData scene in game.GameSave.worldData.gameSceneDataList)
{
if (scene == null || scene.wgoDataList == null)
{
continue;
}
foreach (WgoData chest in scene.wgoDataList)
{
if (chest != null && ((ObjectLinkedToDefinition<WGODef>)(object)chest).Definition != null && ((ObjectLinkedToDefinition<WGODef>)(object)chest).Definition.OpenInMultiInventory && chest.Inventory != null && seen.Add(chest))
{
yield return chest;
}
}
}
}
internal static bool Accepts(WgoData chest, Item item)
{
if (chest == null || chest.Inventory == null || ((ObjectLinkedToDefinition<WGODef>)(object)chest).Definition == null || item == null)
{
return false;
}
ItemDef definition = ((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition;
if (definition != null && ((ItemFilter)((ObjectLinkedToDefinition<WGODef>)(object)chest).Definition.inventoryWhiteList).Contains(definition))
{
return !((ItemFilter)((ObjectLinkedToDefinition<WGODef>)(object)chest).Definition.inventoryBlackList).Contains(definition);
}
return false;
}
internal static int Count(WgoData chest, Item item)
{
return chest.Inventory.Data.GetTotalCountInInventory(((ObjectLinkedToDefinition<ItemDef>)(object)item).id, (Item)null, false);
}
internal static bool HasEnough(Item item, int alreadyCarried)
{
if (item == null)
{
return false;
}
int num = item.Count - alreadyCarried;
if (num <= 0)
{
return true;
}
foreach (WgoData item2 in AllChests())
{
if (Accepts(item2, item))
{
num -= Count(item2, item);
if (num <= 0)
{
return true;
}
}
}
return false;
}
internal static WgoData NearestLocalChest(ZombieWgoData zombie)
{
//IL_003a: 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)
if (((WgoData)zombie).WorldZoneData == null)
{
return null;
}
WgoData result = null;
float num = float.MaxValue;
foreach (WgoData multiInventoryWgoData in ((WgoData)zombie).WorldZoneData.MultiInventoryWgoDatas)
{
if (multiInventoryWgoData != null && multiInventoryWgoData.Inventory != null)
{
float num2 = Vector3.Distance(((WgoData)zombie).Position, multiInventoryWgoData.Position);
if (num2 < num)
{
num = num2;
result = multiInventoryWgoData;
}
}
}
return result;
}
internal static List<WgoData> Sources(Item item, WgoData localTarget)
{
List<WgoData> list = new List<WgoData>();
if (Accepts(localTarget, item) && Count(localTarget, item) > 0)
{
list.Add(localTarget);
}
foreach (WgoData item2 in AllChests())
{
if (item2 != localTarget && Accepts(item2, item) && Count(item2, item) > 0)
{
list.Add(item2);
}
}
return list;
}
}
[HarmonyPatch(typeof(WorldZoneData), "CanDeliveryOrderBeTakenOnExecution")]
internal static class GlobalOrderCheck
{
private static void Postfix(WorldZoneData __instance, DeliveryOrder __0, int __1, ref bool __result)
{
if (!__result && Plugin.Enabled.Value && __0 != null && ((OrderBase)__0).Item != null && __instance.MultiInventoryWgoDatas != null && __instance.MultiInventoryWgoDatas.Count != 0)
{
__result = Supply.HasEnough(((OrderBase)__0).Item, __1);
}
}
}
[HarmonyPatch(typeof(ZombieWgoData), "CaretakerTryMoveToNearestInventoryWithRequiredItemCountToPickUp")]
internal static class ChooseLocalChest
{
private static bool Prefix(ZombieWgoData __instance)
{
if (!Plugin.Enabled.Value)
{
return true;
}
OrderBase value = Traverse.Create((object)__instance).Property("CaretakerExecutingOrder", (object[])null).GetValue<OrderBase>();
if (!(value is DeliveryOrder) || value.Item == null)
{
return true;
}
Item caretakerPortableItem = __instance.CaretakerPortableItem;
int alreadyCarried = ((caretakerPortableItem != null && !caretakerPortableItem.IsEmpty) ? caretakerPortableItem.Count : 0);
if (!Supply.HasEnough(value.Item, alreadyCarried))
{
return true;
}
WgoData val = Supply.NearestLocalChest(__instance);
if (val == null)
{
return true;
}
Traverse.Create((object)__instance).Field("caretakerCurrentTargetUniqueId").SetValue((object)val.UniqueId);
Traverse.Create((object)__instance).Method("CaretakerTryMoveToCurrentTarget", new object[0]).GetValue();
return false;
}
}
[HarmonyPatch(typeof(ZombieWgoData), "CaretakerTryPickUpFromInventory")]
internal static class TakeAcrossZones
{
private static readonly FieldInfo PickupTime = AccessTools.Field(typeof(ZombieWgoData), "caretakerPickingUpFromInventoryTime");
private static bool Prefix(ZombieWgoData __instance, float __0)
{
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
//IL_026d: Unknown result type (might be due to invalid IL or missing references)
//IL_0277: Expected O, but got Unknown
if (!Plugin.Enabled.Value)
{
return true;
}
OrderBase value = Traverse.Create((object)__instance).Property("CaretakerExecutingOrder", (object[])null).GetValue<OrderBase>();
if (!(value is DeliveryOrder) || value.Item == null)
{
return true;
}
Item caretakerPortableItem = __instance.CaretakerPortableItem;
int num = ((caretakerPortableItem != null && !caretakerPortableItem.IsEmpty) ? caretakerPortableItem.Count : 0);
int num2 = value.Item.Count - num;
if (num2 <= 0 || !Supply.HasEnough(value.Item, num))
{
return true;
}
WgoData value2 = Traverse.Create((object)__instance).Property("CaretakerCurrentTarget", (object[])null).GetValue<WgoData>();
if (value2 == null)
{
return true;
}
List<WgoData> list = Supply.Sources(value.Item, value2);
float num3 = (float)PickupTime.GetValue(__instance) + __0;
PickupTime.SetValue(__instance, num3);
float num4 = new LazyExpression(ConstDef.Get("zombie_caretaker_picking_up_time").StringValue).EvaluateFloat((WgoData)(object)__instance);
if (num3 < num4)
{
return false;
}
PickupTime.SetValue(__instance, 0f);
if (!Supply.HasEnough(value.Item, num))
{
return true;
}
int num5 = num2;
List<Tuple<Inventory, List<Item>>> list2 = new List<Tuple<Inventory, List<Item>>>();
foreach (WgoData item in list)
{
int num6 = Math.Min(num5, Supply.Count(item, value.Item));
if (num6 <= 0)
{
continue;
}
List<Item> list3 = item.Inventory.RemoveItemById(((ObjectLinkedToDefinition<ItemDef>)(object)value.Item).id, num6, (Item)null, (Item)null, false);
list2.Add(Tuple.Create<Inventory, List<Item>>(item.Inventory, list3));
if (list3 != null)
{
foreach (Item item2 in list3)
{
num5 -= item2.Count;
}
}
if (num5 <= 0)
{
break;
}
}
if (num5 != 0)
{
foreach (Tuple<Inventory, List<Item>> item3 in list2)
{
if (item3.Item2 == null)
{
continue;
}
foreach (Item item4 in item3.Item2)
{
item3.Item1.AddItemToInventory(item4, (Item)null, false);
}
}
return true;
}
if (num == 0)
{
__instance.CaretakerPortableItem = new Item(((ObjectLinkedToDefinition<ItemDef>)(object)value.Item).id, num2);
}
else
{
caretakerPortableItem.Count += num2;
__instance.CaretakerPortableItem = caretakerPortableItem;
}
__instance.CaretakerState = (ZombieCaretakerState)4;
Traverse.Create((object)__instance).Method("CaretakerTryMoveToZombie", new object[0]).GetValue();
return false;
}
}