InventoryContainer.cs
using Sandbox;
using System;
using System.Text;
public class InventoryContainer
{
/// <summary>Number of columns in the grid.</summary>
public int Width { get; }
/// <summary>Number of rows in the grid.</summary>
public int Height { get; }
/// <summary>Total number of slots (Width * Height).</summary>
public int Capacity => Width * Height;
private readonly InventorySlot[] _slots;
public InventoryContainer( int width, int height )
{
Width = width;
Height = height;
_slots = new InventorySlot[Capacity];
for ( int i = 0; i < Capacity; i++ )
_slots[i] = new InventorySlot();
}
/// <summary>Returns the slot at the given flat index.</summary>
public InventorySlot GetSlot( int index ) => _slots[index];
/// <summary>Returns the slot at grid coordinates (x, y).</summary>
public InventorySlot GetSlot( int x, int y ) => _slots[y * Width + x];
/// <summary>Total number of slots in the container.</summary>
public int SlotCount => _slots.Length;
// ─── Mutating ────────────────────────────────────────────────
/// <summary>
/// Add items to the container, stacking into existing slots first.
/// Returns how many items could not fit (leftover).
/// </summary>
public int AddItem( InventoryItem item, int amount = 1 )
{
// Stack into existing slots first
foreach ( var slot in _slots )
{
if ( !slot.IsEmpty && slot.Item == item )
amount = slot.Add( item, amount );
if ( amount <= 0 ) return 0;
}
// Then fill empty slots
foreach ( var slot in _slots )
{
if ( slot.IsEmpty )
amount = slot.Add( item, amount );
if ( amount <= 0 ) return 0;
}
return amount;
}
/// <summary>
/// Remove items from the container across all stacks.
/// Returns how many were actually removed.
/// </summary>
public int RemoveItem( InventoryItem item, int amount = 1 )
{
int removed = 0;
foreach ( var slot in _slots )
{
if ( slot.IsEmpty || slot.Item != item ) continue;
int take = Math.Min( slot.Quantity, amount - removed );
slot.Remove( take );
removed += take;
if ( removed >= amount ) break;
}
return removed;
}
/// <summary>Returns the total quantity of the given item across all slots.</summary>
public int CountItem( InventoryItem item )
{
int total = 0;
foreach ( var slot in _slots )
if ( !slot.IsEmpty && slot.Item == item )
total += slot.Quantity;
return total;
}
/// <summary>Returns true if the container holds at least the given amount of the item.</summary>
public bool HasItem( InventoryItem item, int amount = 1 )
=> CountItem( item ) >= amount;
/// <summary>Swap the contents of two slots by index.</summary>
public void SwapSlots( int indexA, int indexB )
{
var a = _slots[indexA];
var b = _slots[indexB];
var tempItem = a.Item;
var tempQty = a.Quantity;
var tempDura = a.Durability;
a.Set( b.Item, b.Quantity );
a.Durability = b.Durability;
b.Set( tempItem, tempQty );
b.Durability = tempDura;
}
/// <summary>
/// Sort all slots by the given comparison. Empty slots move to the end.
/// </summary>
public void SortBy( Comparison<InventorySlot> comparison )
{
var nonEmpty = _slots.Where( s => !s.IsEmpty ).ToList();
nonEmpty.Sort( comparison );
int idx = 0;
foreach ( var s in nonEmpty )
{
_slots[idx].Set( s.Item, s.Quantity );
_slots[idx].Durability = s.Durability;
idx++;
}
for ( ; idx < _slots.Length; idx++ )
_slots[idx].Clear();
}
/// <summary>Clears all slots in the container.</summary>
public void Clear()
{
foreach ( var slot in _slots )
slot.Clear();
}
// ─── Network Serialization ───────────────────────────────────
/// <summary>
/// Serialize entire container to a single string for [Sync].
/// Slots are pipe-separated: "item/path:qty|item/path:qty|..."
/// </summary>
public string Serialize()
{
var sb = new StringBuilder();
for ( int i = 0; i < _slots.Length; i++ )
{
if ( i > 0 ) sb.Append( '|' );
sb.Append( _slots[i].Serialize() );
}
return sb.ToString();
}
/// <summary>
/// Restore all slot state from a serialized string.
/// Called on clients when the [Sync] value changes.
/// </summary>
public void Deserialize( string data )
{
if ( string.IsNullOrEmpty( data ) )
{
Clear();
return;
}
var parts = data.Split( '|' );
for ( int i = 0; i < _slots.Length; i++ )
{
_slots[i].Deserialize( i < parts.Length ? parts[i] : "" );
}
}
}