using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.NET.Common;
using Candide.Entities.Controllers.Legacy;
using Candide.Entities.Controllers.Other;
using Candide.Entities.PlayerState;
using Candide.GameModels;
using Candide.Sound;
using Candide.World;
using CandideCreator.Shared.Helpers;
using CandideCreator.Shared.Models;
using CandideServer;
using CandideServer.Entities;
using CandideServer.Entities.ControllerSets.MaterialStorage;
using CandideServer.Entities.Controllers;
using CandideServer.Helpers;
using CandideServer.MessageModels.Entities;
using CandideServer.Models;
using CandideServer.Models.Worlds;
using CandideServer.ServerManagers;
using CandideServer.SimulationModels;
using CandideServer.SyncStrategies;
using CandideServer.World;
using HarmonyLib;
using Microsoft.Xna.Framework;
using Shared.Collision;
using Shared.Data;
using Shared.Entity;
using Shared.Entity.Base;
using Shared.Entity.Components;
using Shared.Helpers;
using Shared.Models.Construction;
using Shared.Models.Interaction;
using Shared.Models.Items;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RequiresPreviewFeatures]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyCompany("BetterCarts")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyInformationalVersion("1.3.1+66a88bdbcb3b3d016bf88b19c0674721f6c23784")]
[assembly: AssemblyProduct("BetterCarts")]
[assembly: AssemblyTitle("BetterCarts")]
[assembly: TargetPlatform("Windows7.0")]
[assembly: SupportedOSPlatform("Windows7.0")]
[assembly: AssemblyVersion("1.3.1.0")]
[module: RefSafetyRules(11)]
namespace BetterCarts
{
internal static class CartAccess
{
internal static readonly Func<ServerCart2Controller, EntityWrapper, bool> PickupEntity = AccessTools.MethodDelegate<Func<ServerCart2Controller, EntityWrapper, bool>>(AccessTools.Method(typeof(ServerCart2Controller), "PickupEntity", (Type[])null, (Type[])null), (object)null, true, (Type[])null);
}
internal sealed class CartTypeRecord
{
internal Guid Id;
internal string Name = string.Empty;
internal ConfigEntry<int> Setting;
}
internal static class CartCapacity
{
internal const int VanillaBase = 4;
internal const int VanillaBlessed = 5;
private const int DefaultCapacity = 4;
private const int SliderMax = 64;
private const string SectionName = "Cart Capacity";
private const string EntryDescription = "The default capacity of this cart is 4.";
private static readonly CartTypeRecord[] Types = new CartTypeRecord[2]
{
new CartTypeRecord
{
Id = new Guid("5af0ea10-21de-404a-a869-b0079653ee0b"),
Name = "Wooden Cart"
},
new CartTypeRecord
{
Id = new Guid("4f26d74b-6fea-4ce9-b369-3bd4507dfff6"),
Name = "Bronze Cart"
}
};
private static readonly Dictionary<Guid, CartTypeRecord> Records = new Dictionary<Guid, CartTypeRecord>();
private static bool _flagsLogged;
internal static bool Enforcing
{
get
{
if (ModConfig.Enabled.Value)
{
return ModConfig.CartCapacityEnabled.Value;
}
return false;
}
}
internal static bool Ejecting
{
get
{
if (Enforcing)
{
if (ModConfig.CartCapacityEjectOverflow != null)
{
return ModConfig.CartCapacityEjectOverflow.Value;
}
return true;
}
return false;
}
}
internal static bool Blessed => WorldFlagsHelper.HasFlag(ServerGameState.WorldFlags, "worship_flag:cart_capacity");
private static int BlessingBonus
{
get
{
if (ModConfig.CartCapacityBlessingBonus != null)
{
return ModConfig.CartCapacityBlessingBonus.Value;
}
return 1;
}
}
internal static void BindTypeEntries(ConfigFile config)
{
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Expected O, but got Unknown
bool hidden = !ModConfig.CartCapacityEnabled.Value;
Records.Clear();
int num = 3;
CartTypeRecord[] types = Types;
foreach (CartTypeRecord cartTypeRecord in types)
{
cartTypeRecord.Setting = config.Bind<int>("Cart Capacity", cartTypeRecord.Id.ToString(), 4, new ConfigDescription("The default capacity of this cart is 4.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), new object[1] { ModConfig.EntryTag(cartTypeRecord.Name + " Capacity", num, hidden) }));
Records[cartTypeRecord.Id] = cartTypeRecord;
num++;
}
}
internal static void LogStartup()
{
ModLog.Info("=== STARTUP ===");
ModLog.Info("Enabled=" + ModConfig.Enabled.Value + " CartCapacityEnabled=" + ModConfig.CartCapacityEnabled.Value + " bonus=" + BlessingBonus + " types=" + Records.Count);
CartTypeRecord[] types = Types;
foreach (CartTypeRecord cartTypeRecord in types)
{
ModLog.Info(" type " + cartTypeRecord.Id.ToString() + " name=\"" + cartTypeRecord.Name + "\" slider=" + ((cartTypeRecord.Setting == null) ? "UNBOUND" : cartTypeRecord.Setting.Value.ToString(CultureInfo.InvariantCulture)));
}
ModLog.Info("=== END STARTUP ===");
}
internal static void NoteWorldLoaded(string origin)
{
if (!_flagsLogged && ModLog.Enabled)
{
_flagsLogged = true;
LogWorldFlags(origin);
}
}
private static void LogWorldFlags(string origin)
{
object worldFlags = ServerGameState.WorldFlags;
string text = ((worldFlags == null) ? "null" : ((!(worldFlags is IEnumerable source) || worldFlags is string) ? worldFlags.ToString() : string.Join(",", from object o in source
select (o != null) ? o.ToString() : "null")));
ModLog.Info("WorldFlags(" + origin + ") key=\"worship_flag:cart_capacity\" HasFlag=" + Blessed + " raw=[" + text + "]");
}
internal static bool TryGetEnforcedCapacity(EntityWrapper cartEntity, out int capacity)
{
capacity = 0;
if (cartEntity == null || !Enforcing)
{
return false;
}
if (!Records.TryGetValue(cartEntity.BaseGuid, out var value) || value.Setting == null)
{
ModLog.AdvancedOnChange("cap:" + cartEntity.BaseGuid, "CAPACITY type=" + cartEntity.BaseGuid.ToString() + " is not a vanilla Cart type - Cart Capacity does not apply to it");
return false;
}
int value2 = value.Setting.Value;
int num = (Blessed ? BlessingBonus : 0);
capacity = value2 + num;
ModLog.AdvancedOnChange("cap:" + value.Id, "CAPACITY " + value.Name + " base=" + value2 + " bonus=" + num + " blessed=" + Blessed + " -> " + capacity);
return true;
}
internal static int GetKnownCapacity(EntityWrapper cartEntity)
{
if (TryGetEnforcedCapacity(cartEntity, out var capacity))
{
return capacity;
}
if (!Blessed)
{
return 4;
}
return 5;
}
}
internal static class CartCargo
{
private sealed class CartState
{
internal readonly List<Guid> Extras = new List<Guid>();
internal long NextSweepTick;
internal bool Adopted;
internal string Written;
internal long OccupiedStamp = -1L;
internal int Occupied;
internal bool EjectDone;
}
private const int SweepIntervalMs = 100;
private const float StackHeight = 6f;
private const float ScanTiles = 2f;
private const float EjectTiles = 1f;
private static readonly string[] SlotKeys = new string[5] { "c1", "c2", "c3", "c4", "c5" };
private static readonly ConditionalWeakTable<ServerCart2Controller, CartState> States = new ConditionalWeakTable<ServerCart2Controller, CartState>();
private static readonly List<EntityWrapper> ReuseCount = new List<EntityWrapper>();
private static readonly List<EntityWrapper> ReuseSweep = new List<EntityWrapper>();
private static readonly List<EntityWrapper> ReuseUnslotted = new List<EntityWrapper>();
private static readonly List<EntityWrapper> ReuseDrop = new List<EntityWrapper>();
private static readonly List<Guid> ReuseOrder = new List<Guid>();
private static readonly List<Guid> ReuseAdopt = new List<Guid>();
private static readonly HashSet<Guid> ReuseSlotted = new HashSet<Guid>();
internal static bool HasFreeSlot(ServerCart2Controller cart)
{
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null || entity.Removed)
{
return false;
}
return GetOccupied(cart) < CartCapacity.GetKnownCapacity(entity);
}
internal static int GetOccupied(ServerCart2Controller cart)
{
CartState orCreateValue = States.GetOrCreateValue(cart);
long tickCount = Environment.TickCount64;
if (orCreateValue.OccupiedStamp == tickCount)
{
return orCreateValue.Occupied;
}
CollectCarried(cart, ReuseCount);
orCreateValue.Occupied = Math.Max(ReuseCount.Count, SlottedCount(cart) + orCreateValue.Extras.Count);
orCreateValue.OccupiedStamp = tickCount;
return orCreateValue.Occupied;
}
internal static void Invalidate(ServerCart2Controller cart)
{
States.GetOrCreateValue(cart).OccupiedStamp = -1L;
}
private static int SlottedCount(ServerCart2Controller cart)
{
EntityWrapper entity = ((AbstractController)cart).Entity;
Parameters parameters = ((AbstractController)cart).Parameters;
Dictionary<string, string> dictionary = ((parameters == null) ? null : parameters.Dictionary);
if (entity == null || dictionary == null)
{
return 0;
}
ReuseSlotted.Clear();
foreach (KeyValuePair<string, string> item in dictionary)
{
if (!string.Equals(item.Key, "bc_cargo", StringComparison.Ordinal) && item.Value != null && item.Value.Length == 36 && Guid.TryParse(item.Value, out var result) && !(result == Guid.Empty))
{
EntityWrapper entityById = entity.System.GetEntityById(result);
if (entityById != null && !entityById.Removed && entityById.Carriable)
{
ReuseSlotted.Add(result);
}
}
}
return ReuseSlotted.Count;
}
internal static bool CanTakeExtra(ServerCart2Controller cart)
{
return States.GetOrCreateValue(cart).Extras.Count < 128;
}
internal static void PinExtra(ServerCart2Controller cart, EntityWrapper item)
{
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity != null && item != null)
{
CartState orCreateValue = States.GetOrCreateValue(cart);
Pin(entity, item);
if (!orCreateValue.Extras.Contains(item.Id))
{
orCreateValue.Extras.Add(item.Id);
ModLog.Advanced("PIN extra " + Short(item.Id) + " on cart " + Short(entity.Id) + " (extras=" + orCreateValue.Extras.Count + ")");
}
orCreateValue.OccupiedStamp = -1L;
Publish(entity, orCreateValue);
}
}
internal static void Tick(ServerCart2Controller cart)
{
CartState orCreateValue = States.GetOrCreateValue(cart);
Adopt(cart, orCreateValue);
HoldExtras(cart, orCreateValue);
long tickCount = Environment.TickCount64;
if (tickCount >= orCreateValue.NextSweepTick)
{
orCreateValue.NextSweepTick = tickCount + 100;
Sweep(cart, orCreateValue);
}
}
internal static void ReleaseAll(ServerCart2Controller cart)
{
CartState orCreateValue = States.GetOrCreateValue(cart);
orCreateValue.Extras.Clear();
orCreateValue.OccupiedStamp = -1L;
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null)
{
return;
}
CollectCarried(cart, ReuseSweep);
foreach (EntityWrapper item in ReuseSweep)
{
if (!IsSlotted(cart, item.Id))
{
Release(entity, item);
}
}
Publish(entity, orCreateValue);
}
private static void Adopt(ServerCart2Controller cart, CartState state)
{
if (state.Adopted)
{
return;
}
state.Adopted = true;
Parameters parameters = ((AbstractController)cart).Parameters;
if (parameters == null)
{
return;
}
string text = parameters.GetString("bc_cargo", string.Empty);
if (!string.IsNullOrEmpty(text))
{
ModLog.Advanced("ADOPT cart=" + Short(((AbstractController)cart).Entity.Id) + " bc_cargo=\"" + text + "\"");
}
CartCargoSync.Unpack(text, ReuseAdopt);
state.Extras.Clear();
foreach (Guid item in ReuseAdopt)
{
state.Extras.Add(item);
}
state.Written = CartCargoSync.Pack(state.Extras);
state.OccupiedStamp = -1L;
}
private static void HoldExtras(ServerCart2Controller cart, CartState state)
{
if (state.Extras.Count == 0)
{
return;
}
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null || entity.Removed)
{
return;
}
bool flag = false;
for (int num = state.Extras.Count - 1; num >= 0; num--)
{
EntityWrapper entityById = entity.System.GetEntityById(state.Extras[num]);
if (entityById == null || entityById.Removed)
{
state.Extras.RemoveAt(num);
flag = true;
}
else if (entityById.CarrierId.HasValue && entityById.CarrierId != entity.Id)
{
ModLog.Advanced("UNPIN extra " + Short(entityById.Id) + " - carrier changed to " + Short(entityById.CarrierId.Value));
entityById.NoEntityCollision = false;
entityById.NoTerrainCollision = false;
state.Extras.RemoveAt(num);
flag = true;
}
else
{
Pin(entity, entityById);
Stack(entity, entityById);
}
}
if (flag)
{
state.OccupiedStamp = -1L;
Publish(entity, state);
}
}
private static void Sweep(ServerCart2Controller cart, CartState state)
{
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null || entity.Removed)
{
return;
}
CollectCarried(cart, ReuseSweep);
DumpSweep(cart, entity);
ReuseUnslotted.Clear();
foreach (EntityWrapper item in ReuseSweep)
{
if (!IsSlotted(cart, item.Id))
{
ReuseUnslotted.Add(item);
}
}
int capacity;
bool flag = CartCapacity.TryGetEnforcedCapacity(entity, out capacity);
int num = ReuseSweep.Count - ReuseUnslotted.Count;
int num2 = (flag ? Math.Max(0, Math.Min(Math.Min(ReuseUnslotted.Count, 128), capacity - num)) : 0);
StableOrder(state, ReuseUnslotted);
state.Extras.Clear();
for (int i = 0; i < ReuseOrder.Count; i++)
{
if (i < num2)
{
state.Extras.Add(ReuseOrder[i]);
continue;
}
EntityWrapper val = Find(ReuseUnslotted, ReuseOrder[i]);
if (val != null)
{
ModLog.Advanced("RELEASE unslotted " + Short(val.Id) + " from cart " + Short(entity.Id) + " (cap=" + (flag ? capacity.ToString() : "none") + " keep=" + num2 + " slotted=" + num + ")");
Release(entity, val);
}
}
bool num3 = !state.EjectDone;
state.EjectDone = true;
if (num3 && flag && CartCapacity.Ejecting)
{
EjectSurplus(cart, state, entity, capacity, num);
}
state.OccupiedStamp = -1L;
Publish(entity, state);
}
private static void EjectSurplus(ServerCart2Controller cart, CartState state, EntityWrapper cartEntity, int capacity, int slotted)
{
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
int num = slotted - capacity;
if (num <= 0)
{
return;
}
ReuseDrop.Clear();
int num2 = SlotKeys.Length - 1;
while (num2 >= 0 && ReuseDrop.Count < num)
{
ref Guid? reference = ref SlotRef(cart, num2);
if (reference.HasValue)
{
EntityWrapper entityById = cartEntity.System.GetEntityById(reference.Value);
if (entityById != null && !entityById.Removed && !(entityById.CarrierId != cartEntity.Id))
{
reference = null;
ServerEntitySystemManager.UpdateEntityParameter(cartEntity, SlotKeys[num2], string.Empty, SyncStrategy.Everyone(), true);
ReuseDrop.Add(entityById);
}
}
num2--;
}
for (int i = 0; i < ReuseDrop.Count; i++)
{
ModLog.Advanced("DROP " + Short(ReuseDrop[i].Id) + " from cart " + Short(cartEntity.Id) + " (slotted=" + slotted + " cap=" + capacity + " surplus=" + num + ")");
Drop(cartEntity, ReuseDrop[i], i, ReuseDrop.Count);
}
ReuseDrop.Clear();
state.OccupiedStamp = -1L;
}
private static ref Guid? SlotRef(ServerCart2Controller cart, int index)
{
return index switch
{
0 => ref cart.Carried1,
1 => ref cart.Carried2,
2 => ref cart.Carried3,
3 => ref cart.Carried4,
_ => ref cart.Carried5,
};
}
private static void Drop(EntityWrapper cartEntity, EntityWrapper item, int index, int count)
{
//IL_0025: 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_0046: 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)
Release(cartEntity, item);
double num = Math.PI * 2.0 * (double)index / (double)count;
float num2 = WorldInfo.TileSize * 1f;
item.Position = cartEntity.Position + new Vector3((float)Math.Cos(num) * num2, (float)Math.Sin(num) * num2, 6f);
item.Velocity = Vector3.Zero;
item.System.CollisionGroup.UpdatePositionAndVelocity(item);
}
private static void DumpSweep(ServerCart2Controller cart, EntityWrapper cartEntity)
{
if (!ModLog.AdvancedEnabled)
{
return;
}
Parameters parameters = ((AbstractController)cart).Parameters;
Dictionary<string, string> dictionary = ((parameters == null) ? null : parameters.Dictionary);
int capacity;
bool flag = CartCapacity.TryGetEnforcedCapacity(cartEntity, out capacity);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("SWEEP cart=").Append(cartEntity.Id).Append(" type=")
.Append(cartEntity.BaseGuid)
.Append(" carried=")
.Append(ReuseSweep.Count)
.Append(" slotted=")
.Append(SlottedCount(cart))
.Append(" cap=")
.Append(flag ? capacity.ToString() : "none")
.Append(" blessed=")
.Append(CartCapacity.Blessed);
stringBuilder.Append(" | fields c1=").Append(Short(cart.Carried1)).Append(" c2=")
.Append(Short(cart.Carried2))
.Append(" c3=")
.Append(Short(cart.Carried3))
.Append(" c4=")
.Append(Short(cart.Carried4))
.Append(" c5=")
.Append(Short(cart.Carried5));
stringBuilder.Append(" | items");
foreach (EntityWrapper item in ReuseSweep)
{
stringBuilder.Append(' ').Append(Short(item.Id)).Append(MatchedKey(dictionary, item.Id));
}
stringBuilder.Append(" | params");
if (dictionary == null || dictionary.Count == 0)
{
stringBuilder.Append(" <none>");
}
else
{
foreach (KeyValuePair<string, string> item2 in dictionary)
{
stringBuilder.Append(' ').Append(item2.Key).Append('=')
.Append((item2.Value.Length == 36) ? Short(item2.Value) : item2.Value);
}
}
ModLog.AdvancedOnChange("sweep:" + cartEntity.Id, stringBuilder.ToString());
}
private static string MatchedKey(IDictionary<string, string> dictionary, Guid itemId)
{
if (dictionary == null)
{
return "(?)";
}
string b = itemId.ToString();
foreach (KeyValuePair<string, string> item in dictionary)
{
if (!string.Equals(item.Key, "bc_cargo", StringComparison.Ordinal) && string.Equals(item.Value, b, StringComparison.OrdinalIgnoreCase))
{
return "(" + item.Key + ")";
}
}
return "(UNSLOTTED)";
}
private static string Short(Guid? id)
{
if (!id.HasValue)
{
return "-";
}
return Short(id.Value.ToString());
}
private static string Short(Guid id)
{
return Short(id.ToString());
}
private static string Short(string id)
{
if (id.Length < 8)
{
return id;
}
return id.Substring(0, 8);
}
private static void Publish(EntityWrapper cartEntity, CartState state)
{
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
string text = CartCargoSync.Pack(state.Extras);
if (!string.Equals(text, state.Written, StringComparison.Ordinal))
{
state.Written = text;
ModLog.Advanced("PUBLISH cart=" + Short(cartEntity.Id) + " bc_cargo=\"" + text + "\"");
ServerEntitySystemManager.UpdateEntityParameter(cartEntity, "bc_cargo", text, SyncStrategy.Everyone(), true);
}
}
private static void StableOrder(CartState state, List<EntityWrapper> items)
{
ReuseOrder.Clear();
foreach (Guid extra in state.Extras)
{
if (Find(items, extra) != null)
{
ReuseOrder.Add(extra);
}
}
foreach (EntityWrapper item in items)
{
if (!ReuseOrder.Contains(item.Id))
{
ReuseOrder.Add(item.Id);
}
}
}
private static EntityWrapper Find(List<EntityWrapper> items, Guid id)
{
foreach (EntityWrapper item in items)
{
if (item.Id == id)
{
return item;
}
}
return null;
}
private static void CollectCarried(ServerCart2Controller cart, List<EntityWrapper> into)
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
into.Clear();
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null)
{
return;
}
CollisionSpecialGroup entityCollisionsOrNull = ServerWorldHandler.GetEntityCollisionsOrNull(entity.WorldId);
if (entityCollisionsOrNull == null)
{
return;
}
int num = (int)(WorldInfo.TileSize * 2f);
Rectangle val = default(Rectangle);
((Rectangle)(ref val))..ctor((int)entity.Position2.X - num, (int)entity.Position2.Y - num, num * 2, num * 2);
entityCollisionsOrNull.GetEntitiesInRectangleArea(RectangleF.op_Implicit(val), into);
for (int num2 = into.Count - 1; num2 >= 0; num2--)
{
EntityWrapper val2 = into[num2];
if (val2 == null || val2.Removed || !ComponentExtension.HasFlags(val2.Mask, (Component)1024) || !val2.Carriable || val2.CarrierId != entity.Id)
{
into.RemoveAt(num2);
}
}
}
private static bool IsSlotted(ServerCart2Controller cart, Guid itemId)
{
Parameters parameters = ((AbstractController)cart).Parameters;
if (parameters == null)
{
return false;
}
Dictionary<string, string> dictionary = parameters.Dictionary;
if (dictionary == null || dictionary.Count == 0)
{
return false;
}
string b = itemId.ToString();
foreach (KeyValuePair<string, string> item in dictionary)
{
if (!string.Equals(item.Key, "bc_cargo", StringComparison.Ordinal) && string.Equals(item.Value, b, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static void Pin(EntityWrapper cartEntity, EntityWrapper item)
{
item.IsThrown = false;
item.ThrowerId = null;
item.NoEntityCollision = true;
item.NoTerrainCollision = true;
item.CarrierId = cartEntity.Id;
}
private static void Release(EntityWrapper cartEntity, EntityWrapper item)
{
if (item.CarrierId == cartEntity.Id)
{
item.CarrierId = null;
}
item.NoEntityCollision = false;
item.NoTerrainCollision = false;
}
private static void Stack(EntityWrapper cartEntity, EntityWrapper item)
{
//IL_0002: 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_001b: 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)
item.Position = cartEntity.Position + new Vector3(0f, 0f, 6f);
item.Velocity = cartEntity.Velocity;
item.System.CollisionGroup.UpdatePositionAndVelocity(item);
}
}
internal static class CartCargoClient
{
private const float RingRadius = 2.5f;
private const float RingHeight = 6f;
private const float RingJitter = 0.02f;
private const int SeatsPerLayer = 4;
private const float LayerStep = 6f;
private const float DepthNudge = 0.02f;
private const string PickupSound = "event:/hits/impact/impact_storage";
private static readonly ConditionalWeakTable<Cart2Controller, List<Guid>> Slots = new ConditionalWeakTable<Cart2Controller, List<Guid>>();
private static readonly List<Guid> ReuseIncoming = new List<Guid>();
private static readonly ConditionalWeakTable<Cart2Controller, string[]> LastRaw = new ConditionalWeakTable<Cart2Controller, string[]>();
internal static void SyncSlots(Cart2Controller cart)
{
List<Guid> orCreateValue = Slots.GetOrCreateValue(cart);
Parameters parameters = ((AbstractController)cart).Parameters;
if (parameters == null)
{
return;
}
string text = parameters.GetString("bc_cargo", string.Empty);
string[] value = LastRaw.GetValue(cart, (Cart2Controller _) => new string[1]);
if (string.Equals(value[0], text, StringComparison.Ordinal))
{
return;
}
bool flag = value[0] == null;
value[0] = text;
ModLog.Advanced("CLIENT SYNC cart=" + ((AbstractController)cart).Entity.Id.ToString() + " bc_cargo=\"" + text + "\" had=" + orCreateValue.Count);
CartCargoSync.Unpack(text, ReuseIncoming);
foreach (Guid item in orCreateValue)
{
if (!ReuseIncoming.Contains(item))
{
ReleaseOne(cart, item);
}
}
bool flag2 = false;
foreach (Guid item2 in ReuseIncoming)
{
if (!orCreateValue.Contains(item2))
{
flag2 = true;
break;
}
}
orCreateValue.Clear();
foreach (Guid item3 in ReuseIncoming)
{
orCreateValue.Add(item3);
}
if (flag2 && !flag)
{
SoundExtensions.EmitSoundOneShot(((AbstractController)cart).Entity, "event:/hits/impact/impact_storage", 1f, 1f);
}
}
internal static void UpdateSlots(Cart2Controller cart)
{
SyncSlots(cart);
List<Guid> orCreateValue = Slots.GetOrCreateValue(cart);
if (orCreateValue.Count == 0)
{
return;
}
for (int num = orCreateValue.Count - 1; num >= 0; num--)
{
if (!GameState.Entities.TryGetValue(orCreateValue[num], out var value))
{
orCreateValue.RemoveAt(num);
}
else if (value.CarrierId == GameState.LocalPlayer.EntityId)
{
ModLog.Advanced("CLIENT yield " + value.Id.ToString() + " to local player");
orCreateValue.RemoveAt(num);
}
else
{
value.IsThrown = false;
value.ThrowerId = null;
value.NoEntityCollision = true;
value.NoTerrainCollision = true;
value.CarrierId = ((AbstractController)cart).Entity.Id;
}
}
for (int i = 0; i < orCreateValue.Count; i++)
{
if (GameState.Entities.TryGetValue(orCreateValue[i], out var value2))
{
Place(cart, value2, i);
}
}
}
internal static void ReleaseAll(Cart2Controller cart)
{
List<Guid> orCreateValue = Slots.GetOrCreateValue(cart);
foreach (Guid item in orCreateValue)
{
ReleaseOne(cart, item);
}
orCreateValue.Clear();
}
private static void ReleaseOne(Cart2Controller cart, Guid id)
{
if (GameState.Entities.TryGetValue(id, out var value))
{
value.NoEntityCollision = false;
value.NoTerrainCollision = false;
if (value.CarrierId == ((AbstractController)cart).Entity.Id)
{
value.CarrierId = null;
}
}
}
private static void Place(Cart2Controller cart, EntityWrapper item, int index)
{
//IL_0008: 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)
//IL_0020: 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_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_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_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: 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_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: 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)
int num = index / 4;
Vector3 val = DirectionToVector3(index % 4) * (2.5f + (float)index * 0.02f);
val.Z = 6f + (float)num * 6f;
val = VectorExtension.ToXzy(val);
Vector3 val2 = default(Vector3);
Vector3.Transform(ref val, ref ((AbstractController)cart).Entity.MeshTransformMatrixRef, ref val2);
item.Position = VectorExtension.ToXzy(val2) + ((AbstractController)cart).Entity.Position + ((AbstractController)cart).Entity.Velocity * ((AbstractController)cart).Entity.Fdt + new Vector3(0f, (float)index * 0.02f, 0f);
item.Velocity = ((AbstractController)cart).Entity.Velocity;
item.System.CollisionGroup.UpdatePositionAndVelocity(item);
}
private static Vector3 DirectionToVector3(float direction)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
float num = direction * ((float)Math.PI / 2f);
return new Vector3((float)Math.Cos(num), (float)Math.Sin(num), 0f);
}
}
internal static class CartCargoSync
{
internal const string CargoKey = "bc_cargo";
internal const int MaxExtras = 128;
private const char Separator = ',';
internal static string Pack(List<Guid> extras)
{
if (extras == null || extras.Count == 0)
{
return string.Empty;
}
StringBuilder stringBuilder = new StringBuilder();
foreach (Guid extra in extras)
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append(',');
}
stringBuilder.Append(extra.ToString());
}
return stringBuilder.ToString();
}
internal static void Unpack(string raw, List<Guid> into)
{
into.Clear();
if (string.IsNullOrEmpty(raw))
{
return;
}
string[] array = raw.Split(',');
foreach (string text in array)
{
if (text.Length != 0 && Guid.TryParse(text, out var result) && !into.Contains(result))
{
into.Add(result);
}
}
}
}
internal static class ModConfig
{
internal static ConfigEntry<bool> Enabled;
internal static ConfigEntry<bool> ChainOverflowEnabled;
internal static ConfigEntry<bool> DepositRangeEnabled;
internal static ConfigEntry<int> DepositRange;
internal static ConfigEntry<bool> CollectRangeEnabled;
internal static ConfigEntry<int> CollectRange;
internal static ConfigEntry<bool> ConnectRangeEnabled;
internal static ConfigEntry<int> ConnectRange;
internal static ConfigEntry<bool> BucketPriorityEnabled;
internal static ConfigEntry<bool> CartReleaseFixEnabled;
internal static ConfigEntry<bool> CartCapacityEnabled;
internal static ConfigEntry<bool> CartCapacityLogging;
internal static ConfigEntry<int> CartCapacityBlessingBonus;
internal static ConfigEntry<bool> CartCapacityEjectOverflow;
internal static ConfigEntry<bool> CartCapacityAdvancedLogging;
internal static ConfigEntry<bool> StockpileRangeEnabled;
internal static ConfigEntry<int> StockpileRange;
internal static ConfigEntry<bool> StockpileWhilePulled;
internal static ConfigEntry<bool> StockpileWhileParked;
internal static void Init(ConfigFile config)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Expected O, but got Unknown
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Expected O, but got Unknown
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Expected O, but got Unknown
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Expected O, but got Unknown
//IL_0175: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: Expected O, but got Unknown
//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
//IL_01c2: Expected O, but got Unknown
//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
//IL_01fe: Expected O, but got Unknown
//IL_0237: Unknown result type (might be due to invalid IL or missing references)
//IL_0241: Expected O, but got Unknown
//IL_027a: Unknown result type (might be due to invalid IL or missing references)
//IL_0284: Expected O, but got Unknown
//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
//IL_02c7: Expected O, but got Unknown
//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0309: Expected O, but got Unknown
//IL_0348: Unknown result type (might be due to invalid IL or missing references)
//IL_0352: Expected O, but got Unknown
//IL_037f: Unknown result type (might be due to invalid IL or missing references)
//IL_0389: Expected O, but got Unknown
//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
//IL_03c0: Expected O, but got Unknown
//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0409: Expected O, but got Unknown
//IL_043b: Unknown result type (might be due to invalid IL or missing references)
//IL_0445: Expected O, but got Unknown
//IL_0470: Unknown result type (might be due to invalid IL or missing references)
//IL_047a: Expected O, but got Unknown
//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
//IL_04af: Expected O, but got Unknown
Enabled = config.Bind<bool>("General", "Enabled", true, new ConfigDescription("Master on/off for the whole mod.", (AcceptableValueBase)null, new object[2]
{
SectionTag("General", 0),
EntryTag("All features", 0)
}));
ChainOverflowEnabled = config.Bind<bool>("Chain Overflow", "Enabled", true, new ConfigDescription("When a full cart picks up an item, the item is passed to the next cart in the chain with a free slot.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Chain Overflow", 1),
EntryTag("Pass overflow along the chain", 0)
}));
CollectRangeEnabled = config.Bind<bool>("Collect Range", "Enabled", true, new ConfigDescription("Carts automatically pick up loose items within range.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Collect Range", 5),
EntryTag("Automatic pickup", 0)
}));
CollectRange = config.Bind<int>("Collect Range", "Range", 2, new ConfigDescription("Collect reach in tiles per side. 0 = vanilla (touch only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[1] { EntryTag("Range", 1) }));
DepositRangeEnabled = config.Bind<bool>("Deposit Range", "Enabled", true, new ConfigDescription("Carts deposit matching cargo into Material Storages within range.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Deposit Range", 6),
EntryTag("Automatic deposit", 0)
}));
DepositRange = config.Bind<int>("Deposit Range", "Range", 2, new ConfigDescription("Deposit reach in tiles per side, 0 = vanilla (park on the storage).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[1] { EntryTag("Range", 1) }));
ConnectRangeEnabled = config.Bind<bool>("Connect Range", "Enabled", true, new ConfigDescription("A free cart is pulled toward a cart the player is pulling once it is within range, so they connect without touching.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Connect Range", 7),
EntryTag("Automatic connect", 0)
}));
ConnectRange = config.Bind<int>("Connect Range", "Range", 2, new ConfigDescription("Connect reach in tiles per side. 0 = vanilla (touch only).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[1] { EntryTag("Range", 1) }));
BucketPriorityEnabled = config.Bind<bool>("Bucket Priority", "Enabled", true, new ConfigDescription("When taking an item off a cart, prefer grabbing an empty bucket over other cargo.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Bucket Priority", 2),
EntryTag("Prefer empty buckets", 0)
}));
CartReleaseFixEnabled = config.Bind<bool>("Cart Release Fix", "Enabled", true, new ConfigDescription("Releasing a pulled cart with the interact key never grabs a different cart on the same press.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Cart Release Fix", 3),
EntryTag("Release without re-grabbing", 0)
}));
CartCapacityEnabled = config.Bind<bool>("Cart Capacity", "Enabled", true, new ConfigDescription("Sets how many items the vanilla carts can carry - modded carts are not supported. A cart's value is its base capacity, and the Mercury blessing adds the bonus below on top, so 0 means a cart that carries nothing. High values can cause stutter and stack the cargo into a tall tower above the cart. Lowering a value stops a cart picking up more straight away, and the next time you load that world the cart drops whatever no longer fits in a circle beside itself. In multiplayer the host's values apply to everyone, and every player needs the mod installed to SEE cargo beyond the normal four. Raising a cart above its normal capacity is the only thing this mod writes to your save: change the cart values and the blessing bonus back to vanilla, leave Eject Overflow on, and load each affected world once before uninstalling.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Cart Capacity", 4),
EntryTag("Set capacity per cart type", 0)
}));
CartCapacityEjectOverflow = config.Bind<bool>("Cart Capacity", "Eject Overflow", true, new ConfigDescription("When a world loads, a cart carrying more than its capacity drops the surplus in a circle beside itself. Turn this off to leave that cargo on the cart, where it stays until you unload it by hand.", (AcceptableValueBase)null, new object[1] { EntryTag("Eject Overflow", 1, !CartCapacityEnabled.Value) }));
CartCapacityBlessingBonus = config.Bind<int>("Cart Capacity", "Blessing Bonus", 1, new ConfigDescription("How much the Mercury cart-capacity blessing adds on top of a cart's base capacity.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), new object[1] { EntryTag("Blessing Bonus", 2, !CartCapacityEnabled.Value) }));
CartCapacityLogging = config.Bind<bool>("Cart Capacity", "Logging", false, new ConfigDescription("DIAGNOSTIC. Writes the Cart Capacity startup and world lines to BepInEx/LogOutput.log. Off by default; turn it on in this file only when reporting a bug.", (AcceptableValueBase)null, new object[1] { EntryTag("Logging (Diagnostic)", 97, hidden: true) }));
CartCapacityAdvancedLogging = config.Bind<bool>("Cart Capacity", "Advanced Logging", false, new ConfigDescription("DIAGNOSTIC. Adds a per-cart and per-pickup trace to the log, which makes it very large. Does nothing while Logging is off.", (AcceptableValueBase)null, new object[1] { EntryTag("Advanced Logging (Diagnostic)", 98, hidden: true) }));
CartCapacity.BindTypeEntries(config);
StockpileRangeEnabled = config.Bind<bool>("Stockpile Range", "Enabled", true, new ConfigDescription("Carts take resources from building output stockpiles within range. Solid resources go into free slots, bucket resources fill empty buckets on the cart.", (AcceptableValueBase)null, new object[2]
{
SectionTag("Stockpile Range", 8),
EntryTag("Take from stockpiles", 0)
}));
StockpileRange = config.Bind<int>("Stockpile Range", "Range", 2, new ConfigDescription("Stockpile reach in tiles per side. 0 = vanilla (off).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), new object[1] { EntryTag("Range", 1) }));
StockpileWhilePulled = config.Bind<bool>("Stockpile Range", "While Pulled", true, new ConfigDescription("Take resources while a player is pulling the cart or its chain.", (AcceptableValueBase)null, new object[1] { EntryTag("While Pulled", 2) }));
StockpileWhileParked = config.Bind<bool>("Stockpile Range", "While Parked", false, new ConfigDescription("Take resources while the cart is parked (not pulled by a player).", (AcceptableValueBase)null, new object[1] { EntryTag("While Parked", 3) }));
}
private static object SectionTag(string section, int order)
{
return new
{
Section = section,
DisplayName = section,
Order = order
};
}
internal static object EntryTag(string displayName, int order)
{
return new
{
DisplayName = displayName,
Order = order
};
}
internal static object EntryTag(string displayName, int order, bool hidden)
{
return new
{
DisplayName = displayName,
Order = order,
Hidden = hidden
};
}
}
internal static class ModLog
{
private const string Tag = "[CC] ";
private static ManualLogSource _log;
private static readonly Dictionary<string, string> LastSeen = new Dictionary<string, string>();
internal static bool Enabled
{
get
{
if (_log != null && ModConfig.CartCapacityLogging != null)
{
return ModConfig.CartCapacityLogging.Value;
}
return false;
}
}
internal static bool AdvancedEnabled
{
get
{
if (Enabled && ModConfig.CartCapacityAdvancedLogging != null)
{
return ModConfig.CartCapacityAdvancedLogging.Value;
}
return false;
}
}
internal static void Init(ManualLogSource log)
{
_log = log;
}
internal static void Info(string message)
{
if (Enabled)
{
_log.LogInfo((object)("[CC] " + message));
}
}
internal static void Advanced(string message)
{
if (AdvancedEnabled)
{
_log.LogInfo((object)("[CC] " + message));
}
}
internal static void OnChange(string key, string message)
{
Emit(Enabled, key, message);
}
internal static void AdvancedOnChange(string key, string message)
{
Emit(AdvancedEnabled, key, message);
}
private static void Emit(bool enabled, string key, string message)
{
if (enabled && (!LastSeen.TryGetValue(key, out var value) || !(value == message)))
{
LastSeen[key] = message;
_log.LogInfo((object)("[CC] " + message));
}
}
internal static void Reset(string key)
{
LastSeen.Remove(key);
}
}
internal static class MsmIntegration
{
private static ManualLogSource _log;
private static ConfigFile _config;
private static bool _registered;
internal static void Init(ManualLogSource log, ConfigFile config)
{
_log = log;
_config = config;
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
if (TryRegisterFrom(assemblies[i]))
{
return;
}
}
_log.LogInfo((object)"Mod Settings Menu not detected at load, using plain config (will register if it loads later).");
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
if (!_registered && TryRegisterFrom(args.LoadedAssembly))
{
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
}
}
private static bool TryRegisterFrom(Assembly assembly)
{
if (assembly == null || assembly.IsDynamic)
{
return false;
}
Type type = null;
Type type2 = null;
Type[] types;
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
types = ex.Types;
}
catch
{
return false;
}
if (types == null)
{
return false;
}
Type[] array = types;
foreach (Type type3 in array)
{
if (!(type3 == null))
{
if (type3.Name == "ModSettingsRegistry")
{
type = type3;
}
else if (type3.Name == "ModSettingsModOptions")
{
type2 = type3;
}
}
}
if (type == null || type2 == null)
{
return false;
}
try
{
Register(type, type2);
_log.LogInfo((object)"Mod Settings Menu detected, mod metadata registered.");
}
catch (Exception ex2)
{
_log.LogWarning((object)("Mod Settings Menu registration failed: " + ex2.Message));
}
_registered = true;
return true;
}
private static void Register(Type registryType, Type optionsType)
{
MethodInfo methodInfo = null;
MethodInfo[] methods = registryType.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (MethodInfo methodInfo2 in methods)
{
if (!(methodInfo2.Name != "Register"))
{
ParameterInfo[] parameters = methodInfo2.GetParameters();
if (parameters.Length == 4 && parameters[3].ParameterType == optionsType)
{
methodInfo = methodInfo2;
break;
}
}
}
if (methodInfo == null)
{
throw new MissingMethodException("ModSettingsRegistry.Register(guid, name, config, ModSettingsModOptions) not found");
}
object obj = Activator.CreateInstance(optionsType);
SetMember(obj, "Version", "1.3.1");
SetMember(obj, "Author", "BeesQ");
SetMember(obj, "Description", "Better Carts makes hauling with Carts more pleasant with quality-of-life features, all configurable in-game");
SetMember(obj, "NexusModsId", 91);
SetMember(obj, "UpdateManifestUrl", "https://raw.githubusercontent.com/BeesQ/romestead-better-carts-mod/main/version.json");
string text = Path.Combine(Path.GetDirectoryName(typeof(BetterCartsPlugin).Assembly.Location) ?? string.Empty, "icon.png");
if (File.Exists(text))
{
SetMember(obj, "IconPath", text);
}
methodInfo.Invoke(null, new object[4] { "com.beesq.romestead.bettercarts", "Better Carts", _config, obj });
}
private static void SetMember(object target, string name, object value)
{
Type type = target.GetType();
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
if (property != null && property.CanWrite)
{
property.SetValue(target, Coerce(value, property.PropertyType));
return;
}
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public);
if (field != null)
{
field.SetValue(target, Coerce(value, field.FieldType));
}
}
private static object Coerce(object value, Type targetType)
{
if (value == null)
{
return null;
}
Type type = Nullable.GetUnderlyingType(targetType) ?? targetType;
if (type.IsInstanceOfType(value))
{
return value;
}
try
{
return Convert.ChangeType(value, type);
}
catch
{
return value;
}
}
}
[BepInPlugin("com.beesq.romestead.bettercarts", "Better Carts", "1.3.1")]
public class BetterCartsPlugin : BasePlugin
{
public const string PluginGuid = "com.beesq.romestead.bettercarts";
public const string PluginName = "Better Carts";
public const string PluginVersion = "1.3.1";
public override void Load()
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
ModLog.Init(((BasePlugin)this).Log);
ModConfig.Init(((BasePlugin)this).Config);
MsmIntegration.Init(((BasePlugin)this).Log, ((BasePlugin)this).Config);
new Harmony("com.beesq.romestead.bettercarts").PatchAll();
((BasePlugin)this).Log.LogInfo((object)"Better Carts 1.3.1 loaded.");
CartCapacity.LogStartup();
}
}
internal static class WorldInfo
{
internal static float TileSize
{
get
{
ChunkedWorld world = ExteriorWorldHandler.World;
if (world != null && world.TileSize.X > 0)
{
return world.TileSize.X;
}
return ChunkedWorld.DefaultWorld.TileSize.X;
}
}
}
}
namespace BetterCarts.Patches
{
[HarmonyPatch(typeof(GrabActionHelper), "TryPlayerGrabActionProximity")]
internal static class BucketPriorityPatch
{
private const float HeightTolerance = 16f;
private const float CargoScanRadius = 32f;
private const float CargoCeiling = 1024f;
private static readonly List<EntityWrapper> Cargo = new List<EntityWrapper>();
private static readonly List<Guid> Extras = new List<Guid>();
private static void Postfix(EntityWrapper grabbingEntity, float radius, ref EntityWrapper __result)
{
if (!ModConfig.Enabled.Value || grabbingEntity == null || grabbingEntity.Removed || (__result != null && !ModConfig.BucketPriorityEnabled.Value))
{
return;
}
EntityWrapper val = ((__result != null) ? CartCarrying(__result) : NearestCart(grabbingEntity, radius));
if (val == null)
{
return;
}
CollectCargo(val);
if (Cargo.Count == 0)
{
return;
}
if (ModConfig.BucketPriorityEnabled.Value)
{
EntityWrapper val2 = Lowest(grabbingEntity, bucketsOnly: true);
if (val2 != null)
{
__result = val2;
return;
}
}
if (__result == null)
{
__result = Lowest(grabbingEntity, bucketsOnly: false);
}
}
private static EntityWrapper CartCarrying(EntityWrapper item)
{
if (!((Enum)item.MaskRef).HasFlag((Enum)(object)(Component)1024))
{
return null;
}
EntityWrapper carrierEntity = item.CarrierEntity;
if (carrierEntity == null || !(carrierEntity.Controller is Cart2Controller))
{
return null;
}
return carrierEntity;
}
private static EntityWrapper NearestCart(EntityWrapper grabbingEntity, float radius)
{
//IL_000f: 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_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
EntityWrapper result = null;
float num = float.MaxValue;
foreach (EntityWrapper item in grabbingEntity.System.GetEntitiesTouchingCircleArea(grabbingEntity.Position2, radius, grabbingEntity.Position.Z, grabbingEntity.Position.Z + 16f))
{
if (item != null && !item.Removed && item.Controller is Cart2Controller)
{
float num2 = Vector2.Distance(item.Position2, grabbingEntity.Position2);
if (num2 < num)
{
num = num2;
result = item;
}
}
}
return result;
}
private static void CollectCargo(EntityWrapper cart)
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
Cargo.Clear();
CartCargoSync.Unpack(cart.Controller.Parameters.GetString("bc_cargo", (string)null), Extras);
foreach (EntityWrapper item in cart.System.GetEntitiesTouchingCircleArea(cart.Position2, 32f, cart.Position.Z, cart.Position.Z + 1024f))
{
if (item != null && !item.Removed && GameState.Entities.ContainsKey(item.Id) && ((Enum)item.MaskRef).HasFlag((Enum)(object)(Component)1024) && item.Carriable && !(item.CarrierId != cart.Id))
{
Cargo.Add(item);
}
}
}
private static EntityWrapper Lowest(EntityWrapper grabbingEntity, bool bucketsOnly)
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
EntityWrapper val = null;
int num = 0;
float num2 = 0f;
foreach (EntityWrapper item in Cargo)
{
if ((!bucketsOnly || IsEmptyBucket(item)) && grabbingEntity.CanAttach(item, false, true))
{
int num3 = Extras.IndexOf(item.Id);
float num4 = Vector2.Distance(item.Position2, grabbingEntity.Position2);
if (val == null || ((num3 == num) ? (num4 < num2) : (num3 < num)))
{
val = item;
num = num3;
num2 = num4;
}
}
}
return val;
}
private static bool IsEmptyBucket(EntityWrapper item)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Invalid comparison between Unknown and I4
AbstractController controller = item.Controller;
BucketController val = (BucketController)(object)((controller is BucketController) ? controller : null);
if (val != null)
{
return (int)val.Content == 0;
}
return false;
}
}
internal static class CartCapacityClientPatch
{
[HarmonyPatch(typeof(Cart2Controller), "OnServerSetState")]
private static class Sync
{
private static void Postfix(Cart2Controller __instance)
{
CartCargoClient.SyncSlots(__instance);
}
}
[HarmonyPatch(typeof(Cart2Controller), "Update", new Type[] { typeof(GameTime) })]
private static class Hold
{
private static void Postfix(Cart2Controller __instance)
{
CartCargoClient.UpdateSlots(__instance);
}
}
[HarmonyPatch(typeof(Cart2Controller), "OnRemove", new Type[] { typeof(EntityRemoveInfo) })]
private static class Release
{
private static void Postfix(Cart2Controller __instance)
{
CartCargoClient.ReleaseAll(__instance);
}
}
[HarmonyPatch(typeof(Cart2Controller), "EntityInitialize")]
private static class Flags
{
private static void Postfix()
{
CartCapacity.NoteWorldLoaded("client");
}
}
}
internal static class CartCapacityPatch
{
[HarmonyPatch(typeof(ServerCart2Controller), "PickupEntity")]
private static class Capacity
{
[HarmonyPriority(800)]
private static bool Prefix(ServerCart2Controller __instance, ref bool __result, out bool __state)
{
__state = false;
if (!CartCapacity.TryGetEnforcedCapacity(((AbstractController)__instance).Entity, out var capacity))
{
return true;
}
int occupied = CartCargo.GetOccupied(__instance);
if (occupied < capacity)
{
return true;
}
__state = true;
__result = false;
ModLog.AdvancedOnChange("block:" + ((AbstractController)__instance).Entity.Id, "BLOCK cart=" + ((AbstractController)__instance).Entity.Id.ToString() + " occupied=" + occupied + " >= cap=" + capacity);
return false;
}
[HarmonyPriority(800)]
private static void Postfix(ServerCart2Controller __instance, EntityWrapper entity, ref bool __result, bool __state)
{
int capacity;
if (__result)
{
CartCargo.Invalidate(__instance);
}
else if (!__state && CartCapacity.TryGetEnforcedCapacity(((AbstractController)__instance).Entity, out capacity) && entity != null && !entity.Removed && !entity.CarrierId.HasValue && CartCargo.CanTakeExtra(__instance) && CartCargo.GetOccupied(__instance) < capacity)
{
ModLog.Advanced("EXTEND cart=" + ((AbstractController)__instance).Entity.Id.ToString() + " taking " + entity.Id.ToString() + " (cap=" + capacity + " occupied=" + CartCargo.GetOccupied(__instance) + ")");
CartCargo.PinExtra(__instance, entity);
__result = true;
}
}
}
[HarmonyPatch(typeof(ServerCart2Controller), "Update", new Type[] { typeof(GameTime) })]
private static class Extras
{
private static void Postfix(ServerCart2Controller __instance)
{
CartCargo.Tick(__instance);
}
}
[HarmonyPatch(typeof(ServerCart2Controller), "OnRemove")]
private static class ReleaseOnRemove
{
private static void Postfix(ServerCart2Controller __instance)
{
CartCargo.ReleaseAll(__instance);
}
}
[HarmonyPatch(typeof(ServerCart2Controller), "EntityInitialize")]
private static class Flags
{
private static void Postfix()
{
CartCapacity.NoteWorldLoaded("server");
}
}
}
internal static class CartReleaseFixPatch
{
[HarmonyPatch(typeof(Cart2Controller), "Update", new Type[] { typeof(GameTime) })]
private static class TrackPulledCart
{
private static void Postfix(Cart2Controller __instance)
{
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (entity != null)
{
if (__instance.FollowingId.HasValue && __instance.FollowingId.Value == GameState.LocalPlayer.EntityId)
{
_pulledCartId = entity.Id;
}
else if (_pulledCartId == entity.Id)
{
_pulledCartId = null;
}
}
}
}
[HarmonyPatch(typeof(Cart2Controller), "GetInteraction", new Type[] { typeof(EntityWrapper) })]
private static class SkipOtherCartWhilePulling
{
private static bool Prefix(Cart2Controller __instance, EntityWrapper otherEntity, ref Interaction __result)
{
if (!ModConfig.Enabled.Value || !ModConfig.CartReleaseFixEnabled.Value)
{
return true;
}
if (!_pulledCartId.HasValue)
{
return true;
}
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (entity == null || entity.Id == _pulledCartId.Value)
{
return true;
}
if (otherEntity == null || otherEntity.Id != GameState.LocalPlayer.EntityId)
{
return true;
}
if (GameState.Entities.TryGetValue(_pulledCartId.Value, out var value) && !value.Removed)
{
AbstractController controller = value.Controller;
Cart2Controller val = (Cart2Controller)(object)((controller is Cart2Controller) ? controller : null);
if (val != null && val.FollowingId.HasValue && !(val.FollowingId.Value != GameState.LocalPlayer.EntityId))
{
__result = null;
return false;
}
}
_pulledCartId = null;
return true;
}
}
private static Guid? _pulledCartId;
}
[HarmonyPatch(typeof(ServerCart2Controller), "PickupEntity")]
internal static class ChainOverflowPatch
{
private const int MaxChainWalkFallback = 256;
[ThreadStatic]
private static bool _walkingChain;
private static void Postfix(ServerCart2Controller __instance, EntityWrapper entity, ref bool __result)
{
if (__result || _walkingChain || !ModConfig.Enabled.Value || !ModConfig.ChainOverflowEnabled.Value)
{
return;
}
_walkingChain = true;
try
{
__result = TryPickupIntoChain(__instance, entity);
}
finally
{
_walkingChain = false;
}
}
private static bool TryPickupIntoChain(ServerCart2Controller source, EntityWrapper entity)
{
HashSet<Guid> visited = new HashSet<Guid> { ((AbstractController)source).Entity.Id };
if (WalkChain(source, entity, visited, followers: true))
{
return true;
}
return WalkChain(source, entity, visited, followers: false);
}
private static bool WalkChain(ServerCart2Controller source, EntityWrapper entity, HashSet<Guid> visited, bool followers)
{
ServerCart2Controller val = source;
for (int i = 0; i < 256; i++)
{
Guid? guid = (followers ? val.FollowerCartId : val.FollowingId);
if (!guid.HasValue || !visited.Add(guid.Value))
{
return false;
}
EntityWrapper entityById = ((AbstractController)source).Entity.System.GetEntityById(guid);
if (entityById == null || entityById.Removed)
{
return false;
}
AbstractController controller = entityById.Controller;
ServerCart2Controller val2 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val2 == null)
{
return false;
}
if (CartAccess.PickupEntity(val2, entity))
{
return true;
}
val = val2;
}
return false;
}
}
[HarmonyPatch(typeof(ServerCart2Controller), "Update", new Type[] { typeof(GameTime) })]
internal static class CollectRangePatch
{
private static void Postfix(ServerCart2Controller __instance)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
if (!ModConfig.Enabled.Value || !ModConfig.CollectRangeEnabled.Value)
{
return;
}
int value = ModConfig.CollectRange.Value;
if (value <= 0)
{
return;
}
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (entity == null || entity.Removed)
{
return;
}
float num = (float)value * WorldInfo.TileSize;
foreach (EntityWrapper item in entity.System.GetEntitiesTouchingCircleArea(entity.Position2, num, entity.Position.Z, entity.Position.Z + 8f))
{
if (ServerCart2Controller.CanBeAutoPicked(item) && !CartAccess.PickupEntity(__instance, item))
{
break;
}
}
}
}
[HarmonyPatch(typeof(ServerCart2Controller), "Update", new Type[] { typeof(GameTime) })]
internal static class ConnectRangePatch
{
private sealed class SpeedHolder
{
public float MoveSpeed;
public float TurnSpeed;
}
private const int MaxChainWalk = 256;
private const float ConnectAccel = 260f;
private const float MaxConnectSpeed = 130f;
private const float TurnAccel = 2600f;
private const float MaxTurnSpeed = 1300f;
private static readonly float TurnAccelRad = MathHelper.ToRadians(2600f);
private static readonly float MaxTurnSpeedRad = MathHelper.ToRadians(1300f);
private static readonly ConditionalWeakTable<ServerCart2Controller, SpeedHolder> Speeds = new ConditionalWeakTable<ServerCart2Controller, SpeedHolder>();
private static void Postfix(ServerCart2Controller __instance)
{
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0146: Unknown result type (might be due to invalid IL or missing references)
//IL_014b: Unknown result type (might be due to invalid IL or missing references)
//IL_0171: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Unknown result type (might be due to invalid IL or missing references)
//IL_0198: Unknown result type (might be due to invalid IL or missing references)
//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
if (__instance == null)
{
return;
}
SpeedHolder orCreateValue = Speeds.GetOrCreateValue(__instance);
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (!ModConfig.Enabled.Value || !ModConfig.ConnectRangeEnabled.Value || ModConfig.ConnectRange.Value <= 0 || entity == null || entity.Removed || __instance.FollowingId.HasValue)
{
if (orCreateValue.MoveSpeed > 0f || orCreateValue.TurnSpeed > 0f)
{
orCreateValue.MoveSpeed = 0f;
orCreateValue.TurnSpeed = 0f;
if (entity != null && !entity.Removed && !__instance.FollowingId.HasValue)
{
entity.Velocity = new Vector3(0f, 0f, entity.Velocity.Z);
}
}
return;
}
EntityWrapper val = FindTarget(entity);
if (val == null)
{
if (orCreateValue.MoveSpeed > 0f || orCreateValue.TurnSpeed > 0f)
{
orCreateValue.MoveSpeed = 0f;
orCreateValue.TurnSpeed = 0f;
entity.Velocity = new Vector3(0f, 0f, entity.Velocity.Z);
}
return;
}
Vector2 val2 = val.Position2 - entity.Position2;
float num = ((Vector2)(ref val2)).Length();
if (!(num <= 0.01f))
{
Vector2 val3 = val2 / num;
orCreateValue.MoveSpeed = Math.Min(orCreateValue.MoveSpeed + 260f * entity.Fdt, 130f);
entity.Velocity = new Vector3(val3.X * orCreateValue.MoveSpeed, val3.Y * orCreateValue.MoveSpeed, entity.Velocity.Z);
float target = VectorExtension.ToFloatDirection(val3);
orCreateValue.TurnSpeed = Math.Min(orCreateValue.TurnSpeed + TurnAccelRad * entity.Fdt, MaxTurnSpeedRad);
entity.Direction = RotateTowards(entity.Direction, target, orCreateValue.TurnSpeed * entity.Fdt, out var reached);
if (reached)
{
orCreateValue.TurnSpeed = 0f;
}
}
}
private static float RotateTowards(float current, float target, float maxDelta, out bool reached)
{
if (maxDelta <= 0f)
{
reached = false;
return current;
}
float value = MathHelper.WrapAngle(target - current);
if (Math.Abs(value) <= maxDelta)
{
reached = true;
return MathHelper.WrapAngle(target);
}
reached = false;
return MathHelper.WrapAngle(current + (float)Math.Sign(value) * maxDelta);
}
private static EntityWrapper FindTarget(EntityWrapper cart)
{
//IL_0023: 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_0035: 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)
int value = ModConfig.ConnectRange.Value;
float num = (float)value * WorldInfo.TileSize;
EntityWrapper result = null;
float num2 = float.MaxValue;
foreach (EntityWrapper item in cart.System.GetEntitiesTouchingCircleArea(cart.Position2, num, cart.Position.Z, cart.Position.Z + 8f))
{
if (item == null || item.Removed || item.Id == cart.Id)
{
continue;
}
AbstractController controller = item.Controller;
ServerCart2Controller val = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val == null || val.FollowerCartId.HasValue)
{
continue;
}
int pulledCartChainLength = GetPulledCartChainLength(val, cart.System);
int effectiveConnectRange = GetEffectiveConnectRange(value, pulledCartChainLength);
if (effectiveConnectRange > 0)
{
float num3 = (float)effectiveConnectRange * WorldInfo.TileSize;
float num4 = num3 * num3;
float num5 = Vector2.DistanceSquared(item.Position2, cart.Position2);
if (!(num5 > num4) && num5 < num2)
{
num2 = num5;
result = item;
}
}
}
return result;
}
private static int GetEffectiveConnectRange(int maxRangeTiles, int chainLength)
{
if (maxRangeTiles <= 0 || chainLength <= 0)
{
return 0;
}
return Math.Min(maxRangeTiles, chainLength);
}
private static int GetPulledCartChainLength(ServerCart2Controller cart, EntitySystem system)
{
HashSet<Guid> hashSet = new HashSet<Guid>();
ServerCart2Controller val = cart;
int num = 0;
for (int i = 0; i < 256; i++)
{
num++;
Guid? followingId = val.FollowingId;
if (!followingId.HasValue)
{
return 0;
}
if (!hashSet.Add(followingId.Value))
{
return 0;
}
EntityWrapper entityById = system.GetEntityById(followingId);
if (entityById == null || entityById.Removed)
{
return 0;
}
AbstractController controller = entityById.Controller;
ServerCart2Controller val2 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val2 == null)
{
return num;
}
val = val2;
}
return 0;
}
}
[HarmonyPatch(typeof(ServerMaterialStoragePitController), "Update", new Type[] { typeof(GameTime) })]
internal static class DepositRangePatch
{
private sealed class TimerHolder
{
public float Value;
}
private const float CheckTime = 0.1f;
private const float MaxDepositZ = 24f;
private static readonly ConditionalWeakTable<ServerMaterialStoragePitController, TimerHolder> Timers = new ConditionalWeakTable<ServerMaterialStoragePitController, TimerHolder>();
private static readonly List<EntityWrapper> ReuseList = new List<EntityWrapper>();
private static readonly FieldRef<ServerMaterialStoragePitController, Rectangle> WorldBoundsRef = AccessTools.FieldRefAccess<ServerMaterialStoragePitController, Rectangle>("_worldBounds");
private static readonly FieldRef<ServerMaterialStoragePitController, IOStorageType> StorageTypeRef = AccessTools.FieldRefAccess<ServerMaterialStoragePitController, IOStorageType>("_storageType");
private static void Postfix(ServerMaterialStoragePitController __instance)
{
//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: 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_01a6: Unknown result type (might be due to invalid IL or missing references)
//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
//IL_022d: Unknown result type (might be due to invalid IL or missing references)
if (!ModConfig.Enabled.Value || !ModConfig.DepositRangeEnabled.Value)
{
return;
}
int value = ModConfig.DepositRange.Value;
if (value <= 0 || StorageTypeRef.Invoke(__instance))
{
return;
}
BuildingSimulationModel currentlyUpdatingBuilding = ServerTempState.CurrentlyUpdatingBuilding;
if (currentlyUpdatingBuilding == null || !currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.HasValue)
{
return;
}
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (entity == null || entity.Removed)
{
return;
}
TimerHolder orCreateValue = Timers.GetOrCreateValue(__instance);
orCreateValue.Value -= entity.Fdt;
if (orCreateValue.Value > 0f)
{
return;
}
orCreateValue.Value += 0.1f;
Guid? parentWorldId = ServerTempState.CurrentlyUpdatingWorldModel.ParentWorldId;
if (!parentWorldId.HasValue)
{
return;
}
CollisionSpecialGroup entityCollisionsOrNull = ServerWorldHandler.GetEntityCollisionsOrNull(parentWorldId.Value);
InternalResourceStorageModel val = default(InternalResourceStorageModel);
if (entityCollisionsOrNull == null || !ServerGameState.TryGetResourceStorage(currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.Value, ref val))
{
return;
}
int num = (int)((float)value * WorldInfo.TileSize);
Rectangle val2 = WorldBoundsRef.Invoke(__instance);
Rectangle val3 = val2;
((Rectangle)(ref val3)).Inflate(num, num);
ReuseList.Clear();
entityCollisionsOrNull.GetEntitiesInRectangleArea(RectangleF.op_Implicit(val3), ReuseList);
foreach (EntityWrapper reuse in ReuseList)
{
if (reuse.PositionZ > 24f || reuse.Id == entity.Id || !((Enum)reuse.MaskRef).HasFlag((Enum)(object)(Component)1024) || !reuse.Carriable || ((Rectangle)(ref val2)).Contains((int)reuse.Position2.X, (int)reuse.Position2.Y))
{
continue;
}
EntityWrapper carrierEntity = reuse.CarrierEntity;
if (carrierEntity != null && carrierEntity.Controller is ServerCart2Controller && reuse.ConstructionMaterials != null && !reuse.ConstructionMaterials.IsEmpty && val.CanAdjustByConstructionMaterialsAggregate(reuse.ConstructionMaterials))
{
ServerEntityModel serverModelFromWrapperOrNull = ServerEntityHelper.GetServerModelFromWrapperOrNull(reuse);
if (serverModelFromWrapperOrNull != null && BuildingsServerManager.TryDepositResourceEntityIntoStorage(serverModelFromWrapperOrNull, currentlyUpdatingBuilding.InstanceModel.Id))
{
ServerSendMessageHelper.PlaySoundOnPosition(serverModelFromWrapperOrNull.Position, serverModelFromWrapperOrNull.WorldId, "event:/hits/impact/impact_storage", 1f, 1f);
break;
}
}
}
}
}
internal static class StockpileRangePatch
{
private sealed class TimerHolder
{
public float Value;
}
private sealed class FillState
{
public long NextTick;
}
[HarmonyPatch(typeof(ServerMaterialStorageStackController), "Update", new Type[] { typeof(GameTime) })]
private static class TakeSolidsFromOutputStacks
{
private static void Postfix(ServerMaterialStorageStackController __instance)
{
//IL_019a: Unknown result type (might be due to invalid IL or missing references)
//IL_019f: Unknown result type (might be due to invalid IL or missing references)
//IL_020f: Unknown result type (might be due to invalid IL or missing references)
//IL_0214: Unknown result type (might be due to invalid IL or missing references)
//IL_021b: Unknown result type (might be due to invalid IL or missing references)
//IL_0222: Unknown result type (might be due to invalid IL or missing references)
//IL_0227: Unknown result type (might be due to invalid IL or missing references)
//IL_022c: Unknown result type (might be due to invalid IL or missing references)
//IL_0238: Unknown result type (might be due to invalid IL or missing references)
//IL_0249: Unknown result type (might be due to invalid IL or missing references)
//IL_0272: Unknown result type (might be due to invalid IL or missing references)
//IL_0279: Unknown result type (might be due to invalid IL or missing references)
//IL_0291: Unknown result type (might be due to invalid IL or missing references)
//IL_0293: Unknown result type (might be due to invalid IL or missing references)
//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
//IL_01bf: 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_035c: Unknown result type (might be due to invalid IL or missing references)
//IL_035e: Unknown result type (might be due to invalid IL or missing references)
//IL_0389: Unknown result type (might be due to invalid IL or missing references)
//IL_038e: Unknown result type (might be due to invalid IL or missing references)
//IL_0391: Unknown result type (might be due to invalid IL or missing references)
//IL_0396: 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_039c: Unknown result type (might be due to invalid IL or missing references)
//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
//IL_03bb: Expected O, but got Unknown
//IL_0303: Unknown result type (might be due to invalid IL or missing references)
//IL_0308: Unknown result type (might be due to invalid IL or missing references)
if (!ModConfig.Enabled.Value || !ModConfig.StockpileRangeEnabled.Value)
{
return;
}
int value = ModConfig.StockpileRange.Value;
if (value <= 0)
{
return;
}
bool value2 = ModConfig.StockpileWhilePulled.Value;
bool value3 = ModConfig.StockpileWhileParked.Value;
if ((!value2 && !value3) || (int)StorageTypeRef.Invoke(__instance) != 1)
{
return;
}
BuildingSimulationModel currentlyUpdatingBuilding = ServerTempState.CurrentlyUpdatingBuilding;
if (currentlyUpdatingBuilding == null || !currentlyUpdatingBuilding.InstanceModel.OutputResourceStorageId.HasValue)
{
return;
}
Guid value4 = currentlyUpdatingBuilding.InstanceModel.OutputResourceStorageId.Value;
if (currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.HasValue && currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.Value == value4)
{
return;
}
EntityWrapper entity = ((AbstractController)__instance).Entity;
if (entity == null || entity.Removed)
{
return;
}
TimerHolder orCreateValue = StackTimers.GetOrCreateValue(__instance);
orCreateValue.Value -= entity.Fdt;
if (orCreateValue.Value > 0f)
{
return;
}
orCreateValue.Value += 0.1f;
WorldModel currentlyUpdatingWorldModel = ServerTempState.CurrentlyUpdatingWorldModel;
if (currentlyUpdatingWorldModel == null)
{
return;
}
Guid guid = currentlyUpdatingWorldModel.ParentWorldId ?? currentlyUpdatingWorldModel.Id;
InternalResourceStorageModel val = default(InternalResourceStorageModel);
if (!ServerGameState.TryGetResourceStorage(value4, ref val))
{
return;
}
string text = null;
Guid entityBaseId = Guid.Empty;
foreach (KeyValuePair<string, int> resourceAmount in val.ResourceAmounts)
{
if (resourceAmount.Value > 0 && !IsBucketResource(resourceAmount.Key))
{
ConstructionResourceDataModel? constructionResourceOrNull = ConstructionResourcesDataBase.GetConstructionResourceOrNull(resourceAmount.Key);
if (constructionResourceOrNull.HasValue && constructionResourceOrNull.Value.DefaultBaseGuid.HasValue)
{
text = resourceAmount.Key;
entityBaseId = constructionResourceOrNull.Value.DefaultBaseGuid.Value;
break;
}
}
}
if (text == null)
{
return;
}
CollisionSpecialGroup entityCollisionsOrNull = ServerWorldHandler.GetEntityCollisionsOrNull(guid);
if (entityCollisionsOrNull == null)
{
return;
}
float tileSize = WorldInfo.TileSize;
Vector3 val2 = VectorExtension.ToVector3Xy(((Rectangle)(ref currentlyUpdatingBuilding.InstanceModel.TileBounds)).Location) * tileSize + entity.Position;
int num = (int)((float)value * tileSize);
Rectangle val3 = default(Rectangle);
((Rectangle)(ref val3))..ctor((int)(val2.X - tileSize * 0.5f), (int)(val2.Y - tileSize * 0.5f), (int)tileSize, (int)tileSize);
((Rectangle)(ref val3)).Inflate(num, num);
Vector2 val4 = default(Vector2);
((Vector2)(ref val4))..ctor(val2.X, val2.Y);
ReuseList.Clear();
entityCollisionsOrNull.GetEntitiesInRectangleArea(RectangleF.op_Implicit(val3), ReuseList);
ServerCart2Controller val5 = null;
float num2 = float.MaxValue;
foreach (EntityWrapper reuse in ReuseList)
{
if (reuse.Removed || reuse.PositionZ > 24f)
{
continue;
}
AbstractController controller = reuse.Controller;
ServerCart2Controller val6 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val6 != null && IsEligible(val6, value2, value3) && HasChainCapacity(val6))
{
float num3 = Vector2.DistanceSquared(reuse.Position2, val4);
if (num3 < num2)
{
num2 = num3;
val5 = val6;
}
}
}
if (val5 != null)
{
ReuseTake[0] = new ResourceAmount
{
ResourceId = text,
Amount = 1
};
if (InternalResourceStorageServerManager.TryRemoveResources_Destructive(val, ReuseTake))
{
EntityWrapper entity2 = ((AbstractController)val5).Entity;
ReuseSpawnList.Clear();
ReuseSpawnList.Add(new RequestSpawnEntityMessage
{
Position = entity2.Position,
Velocity = Vector3.Zero,
WorldId = guid,
EntityBaseId = entityBaseId
});
EntityServerManager.SpawnEntities((IReadOnlyCollection<RequestSpawnEntityMessage>)ReuseSpawnList, guid);
}
}
}
}
[HarmonyPatch(typeof(AbstractController), "Update", new Type[] { typeof(GameTime) })]
private static class FillBucketsFromFluidVats
{
private static void Postfix(AbstractController __instance)
{
ServerMaterialStorageFluidContainerController val = (ServerMaterialStorageFluidContainerController)(object)((__instance is ServerMaterialStorageFluidContainerController) ? __instance : null);
if (val != null)
{
TryFillBuckets(val);
}
}
}
private const int FillIntervalMs = 100;
private const float CheckTime = 0.1f;
private const float MaxTakeZ = 24f;
private const int MaxChainWalk = 256;
private static readonly ConditionalWeakTable<ServerMaterialStorageStackController, TimerHolder> StackTimers = new ConditionalWeakTable<ServerMaterialStorageStackController, TimerHolder>();
private static readonly ConditionalWeakTable<object, FillState> FillTimers = new ConditionalWeakTable<object, FillState>();
private static readonly List<EntityWrapper> ReuseList = new List<EntityWrapper>();
private static readonly List<EntityWrapper> ReuseCarriedList = new List<EntityWrapper>();
private static readonly List<RequestSpawnEntityMessage> ReuseSpawnList = new List<RequestSpawnEntityMessage>();
private static readonly ResourceAmount[] ReuseTake = (ResourceAmount[])(object)new ResourceAmount[1];
private static readonly HashSet<Guid> ReuseVisited = new HashSet<Guid>();
private static readonly HashSet<string> BucketResources = new HashSet<string> { "resource:clay", "resource:ash", "resource:water" };
private static readonly FieldRef<ServerMaterialStorageStackController, IOStorageType> StorageTypeRef = AccessTools.FieldRefAccess<ServerMaterialStorageStackController, IOStorageType>("_storageType");
private static void TryFillBuckets(ServerMaterialStorageFluidContainerController vat)
{
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
//IL_01af: Unknown result type (might be due to invalid IL or missing references)
//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
//IL_021e: Unknown result type (might be due to invalid IL or missing references)
//IL_0220: Unknown result type (might be due to invalid IL or missing references)
//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
//IL_031f: Unknown result type (might be due to invalid IL or missing references)
//IL_0290: Unknown result type (might be due to invalid IL or missing references)
//IL_0295: Unknown result type (might be due to invalid IL or missing references)
if (!ModConfig.Enabled.Value || !ModConfig.StockpileRangeEnabled.Value)
{
return;
}
int value = ModConfig.StockpileRange.Value;
if (value <= 0)
{
return;
}
bool value2 = ModConfig.StockpileWhilePulled.Value;
bool value3 = ModConfig.StockpileWhileParked.Value;
if (!value2 && !value3)
{
return;
}
BuildingSimulationModel currentlyUpdatingBuilding = ServerTempState.CurrentlyUpdatingBuilding;
if (currentlyUpdatingBuilding == null || !currentlyUpdatingBuilding.InstanceModel.OutputResourceStorageId.HasValue)
{
return;
}
Guid value4 = currentlyUpdatingBuilding.InstanceModel.OutputResourceStorageId.Value;
if (currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.HasValue && currentlyUpdatingBuilding.InstanceModel.ResourceStorageId.Value == value4)
{
return;
}
EntityWrapper entity = ((AbstractController)vat).Entity;
if (entity == null || entity.Removed)
{
return;
}
FillState orCreateValue = FillTimers.GetOrCreateValue(vat);
long tickCount = Environment.TickCount64;
if (tickCount < orCreateValue.NextTick)
{
return;
}
orCreateValue.NextTick = tickCount + 100;
WorldModel currentlyUpdatingWorldModel = ServerTempState.CurrentlyUpdatingWorldModel;
if (currentlyUpdatingWorldModel == null)
{
return;
}
Guid guid = currentlyUpdatingWorldModel.ParentWorldId ?? currentlyUpdatingWorldModel.Id;
InternalResourceStorageModel val = default(InternalResourceStorageModel);
if (!ServerGameState.TryGetResourceStorage(value4, ref val))
{
return;
}
string text = null;
foreach (KeyValuePair<string, int> resourceAmount in val.ResourceAmounts)
{
if (resourceAmount.Value > 0 && IsBucketResource(resourceAmount.Key))
{
text = resourceAmount.Key;
break;
}
}
if (text == null)
{
return;
}
CollisionSpecialGroup entityCollisionsOrNull = ServerWorldHandler.GetEntityCollisionsOrNull(guid);
if (entityCollisionsOrNull == null)
{
return;
}
float tileSize = WorldInfo.TileSize;
Vector3 val2 = VectorExtension.ToVector3Xy(((Rectangle)(ref currentlyUpdatingBuilding.InstanceModel.TileBounds)).Location) * tileSize + entity.Position;
int num = (int)((float)value * tileSize);
Rectangle val3 = default(Rectangle);
((Rectangle)(ref val3))..ctor((int)(val2.X - tileSize * 0.5f), (int)(val2.Y - tileSize * 0.5f), (int)tileSize, (int)tileSize);
((Rectangle)(ref val3)).Inflate(num, num);
Vector2 val4 = default(Vector2);
((Vector2)(ref val4))..ctor(val2.X, val2.Y);
ReuseList.Clear();
entityCollisionsOrNull.GetEntitiesInRectangleArea(RectangleF.op_Implicit(val3), ReuseList);
ServerCart2Controller val5 = null;
float num2 = float.MaxValue;
foreach (EntityWrapper reuse in ReuseList)
{
if (reuse.Removed || reuse.PositionZ > 24f)
{
continue;
}
AbstractController controller = reuse.Controller;
ServerCart2Controller val6 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val6 != null && IsEligible(val6, value2, value3) && FindEmptyBucketInChain(val6) != null)
{
float num3 = Vector2.DistanceSquared(reuse.Position2, val4);
if (num3 < num2)
{
num2 = num3;
val5 = val6;
}
}
}
if (val5 == null)
{
return;
}
ServerBucketController val7 = FindEmptyBucketInChain(val5);
if (val7 != null)
{
ReuseTake[0] = new ResourceAmount
{
ResourceId = text,
Amount = 1
};
if (InternalResourceStorageServerManager.TryRemoveResources_Destructive(val, ReuseTake))
{
val7.SetContentType((BucketContentType)1, text, 1, true);
ServerSendMessageHelper.PlaySoundOnPosition(((AbstractController)val7).Entity.Position, guid, GetFillSound(text), 1f, 1f);
}
}
}
private static ServerBucketController FindEmptyBucketInChain(ServerCart2Controller cart)
{
ServerBucketController val = FindEmptyBucketOnCart(cart);
if (val != null)
{
return val;
}
if (!ModConfig.ChainOverflowEnabled.Value)
{
return null;
}
ReuseVisited.Clear();
ReuseVisited.Add(((AbstractController)cart).Entity.Id);
val = WalkFindEmptyBucket(cart, followers: true);
if (val != null)
{
return val;
}
return WalkFindEmptyBucket(cart, followers: false);
}
private static ServerBucketController WalkFindEmptyBucket(ServerCart2Controller source, bool followers)
{
ServerCart2Controller val = source;
for (int i = 0; i < 256; i++)
{
Guid? guid = (followers ? val.FollowerCartId : val.FollowingId);
if (!guid.HasValue || !ReuseVisited.Add(guid.Value))
{
return null;
}
EntityWrapper entityById = ((AbstractController)source).Entity.System.GetEntityById(guid);
if (entityById == null || entityById.Removed)
{
return null;
}
AbstractController controller = entityById.Controller;
ServerCart2Controller val2 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val2 == null)
{
return null;
}
ServerBucketController val3 = FindEmptyBucketOnCart(val2);
if (val3 != null)
{
return val3;
}
val = val2;
}
return null;
}
private static ServerBucketController FindEmptyBucketOnCart(ServerCart2Controller cart)
{
//IL_0035: 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_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_00aa: Unknown result type (might be due to invalid IL or missing references)
EntityWrapper entity = ((AbstractController)cart).Entity;
if (entity == null || entity.Removed)
{
return null;
}
CollisionSpecialGroup entityCollisionsOrNull = ServerWorldHandler.GetEntityCollisionsOrNull(entity.WorldId);
if (entityCollisionsOrNull == null)
{
return null;
}
int num = (int)(WorldInfo.TileSize * 2f);
Rectangle val = default(Rectangle);
((Rectangle)(ref val))..ctor((int)entity.Position2.X - num, (int)entity.Position2.Y - num, num * 2, num * 2);
ReuseCarriedList.Clear();
entityCollisionsOrNull.GetEntitiesInRectangleArea(RectangleF.op_Implicit(val), ReuseCarriedList);
foreach (EntityWrapper reuseCarried in ReuseCarriedList)
{
if (reuseCarried.Removed)
{
continue;
}
AbstractController controller = reuseCarried.Controller;
ServerBucketController val2 = (ServerBucketController)(object)((controller is ServerBucketController) ? controller : null);
if (val2 != null && (int)val2.Content == 0)
{
EntityWrapper carrierEntity = reuseCarried.CarrierEntity;
if (carrierEntity != null && !(carrierEntity.Id != entity.Id))
{
return val2;
}
}
}
return null;
}
private static bool IsBucketResource(string resourceId)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
if (BucketResources.Contains(resourceId))
{
return true;
}
ConstructionResourceDataModel? constructionResourceOrNull = ConstructionResourcesDataBase.GetConstructionResourceOrNull(resourceId);
if (constructionResourceOrNull.HasValue)
{
return !constructionResourceOrNull.Value.DefaultBaseGuid.HasValue;
}
return true;
}
private static bool IsEligible(ServerCart2Controller cart, bool whilePulled, bool whileParked)
{
if (!IsChainPulled(cart))
{
return whileParked;
}
return whilePulled;
}
private static bool IsChainPulled(ServerCart2Controller cart)
{
ReuseVisited.Clear();
ServerCart2Controller val = cart;
for (int i = 0; i < 256; i++)
{
Guid? followingId = val.FollowingId;
if (!followingId.HasValue || !ReuseVisited.Add(followingId.Value))
{
return false;
}
EntityWrapper entityById = ((AbstractController)cart).Entity.System.GetEntityById(followingId);
if (entityById == null || entityById.Removed)
{
return false;
}
AbstractController controller = entityById.Controller;
ServerCart2Controller val2 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val2 == null)
{
return true;
}
val = val2;
}
return false;
}
private static bool HasChainCapacity(ServerCart2Controller cart)
{
if (HasFreeSlot(cart))
{
return true;
}
if (!ModConfig.ChainOverflowEnabled.Value)
{
return false;
}
ReuseVisited.Clear();
ReuseVisited.Add(((AbstractController)cart).Entity.Id);
if (!WalkHasFreeSlot(cart, followers: true))
{
return WalkHasFreeSlot(cart, followers: false);
}
return true;
}
private static bool WalkHasFreeSlot(ServerCart2Controller source, bool followers)
{
ServerCart2Controller val = source;
for (int i = 0; i < 256; i++)
{
Guid? guid = (followers ? val.FollowerCartId : val.FollowingId);
if (!guid.HasValue || !ReuseVisited.Add(guid.Value))
{
return false;
}
EntityWrapper entityById = ((AbstractController)source).Entity.System.GetEntityById(guid);
if (entityById == null || entityById.Removed)
{
return false;
}
AbstractController controller = entityById.Controller;
ServerCart2Controller val2 = (ServerCart2Controller)(object)((controller is ServerCart2Controller) ? controller : null);
if (val2 == null)
{
return false;
}
if (HasFreeSlot(val2))
{
return true;
}
val = val2;
}
return false;
}
private static bool HasFreeSlot(ServerCart2Controller cart)
{
return CartCargo.HasFreeSlot(cart);
}
private static string GetFillSound(string resourceId)
{
if (resourceId == "resource:ash")
{
return "event:/items/bucket/fill_bucket_sand";
}
if (resourceId == "resource:water")
{
return "event:/items/bucket/fill_bucket_water";
}
return "event:/items/bucket/fill_bucket_construction_material";
}
}
}