context: #region References
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  
  using Server.ContextMenus;
  using Server.Network;
  #endregion
  
  namespace Server.Items
  {
      public delegate void OnItemConsumed(Item item, int amount);
  
      public delegate int CheckItemGroup(Item a, Item b);
  
      public delegate void ContainerSnoopHandler(Container cont, Mobile from);
  
      public class Container : Item
      {
          #region Enhanced Client Support
          public virtual void ValidateGridLocation(Item item)
          {
  			var pos = item.GridLocation;
  
              if (!IsFreePosition(pos))
              {
                  item.GridLocation = GetNewPosition(pos);
              }
          }
  
          public virtual bool IsFreePosition(byte pos)
          {
              if (pos < 0 || pos > 0x7C)
              {
                  return false;
              }
  
  			return Items.All(i => i.GridLocation != pos);
          }
  
          public virtual byte GetNewPosition(byte current)
          {
              int index = 0;
              byte next = (byte)(current + 1);
  
              while (++index < 0x7D)
              {
                  if (!IsFreePosition(next))
                  {
                      if (next == 0x7C)
                      {
                          next = 0;
  
                          if (IsFreePosition(next))
                          {
                              return next;
                          }
                      }
                  }
                  else
                  {
                      return next;
                  }
  
                  next++;
              }
  
              return 0;
          }
  
          public virtual void ValidatePositions()
          {
              foreach (var item in Items)
              {
                  if (IsFreePosition(item.GridLocation))
                  {
                      item.GridLocation = GetNewPosition(item.GridLocation);
                  }
              }
          }
          #endregion
  
          private static ContainerSnoopHandler m_SnoopHandler;
  
          public static ContainerSnoopHandler SnoopHandler { get { return m_SnoopHandler; } set { m_SnoopHandler = value; } }
  
          private ContainerData m_ContainerData;
  
          private int m_DropSound;
          private int m_GumpID;
          private int m_MaxItems;
  
          private int m_TotalItems;
          private int m_TotalWeight;
          private int m_TotalGold;
  
          private bool m_LiftOverride;
  
          internal List<Item> m_Items;
  
          public ContainerData ContainerData
          {
              get
              {
                  if (m_ContainerData == null)
                  {
                      UpdateContainerData();
                  }
  
                  return m_ContainerData;
              }
              set { m_ContainerData = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public override int ItemID
          {
              get { return base.ItemID; }
              set
              {
                  int oldID = ItemID;
  
                  base.ItemID = value;
  
                  if (ItemID != oldID)
                  {
                      UpdateContainerData();
                  }
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int GumpID { get { return (m_GumpID == -1 ? DefaultGumpID : m_GumpID); } set { m_GumpID = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int DropSound { get { return (m_DropSound == -1 ? DefaultDropSound : m_DropSound); } set { m_DropSound = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int MaxItems
          {
              get { return (m_MaxItems == -1 ? DefaultMaxItems : m_MaxItems); }
              set
              {
                  m_MaxItems = value;
                  InvalidateProperties();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public virtual int MaxWeight
          {
              get
              {
                  if (Parent is Container && ((Container)Parent).MaxWeight == 0)
                  {
                      return 0;
                  }
                  else
                  {
                      return DefaultMaxWeight;
                  }
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool LiftOverride { get { return m_LiftOverride; } set { m_LiftOverride = value; } }
  
          public virtual void UpdateContainerData()
          {
              ContainerData = ContainerData.GetData(ItemID);
          }
  
          public virtual Rectangle2D Bounds { get { return ContainerData.Bounds; } }
          public virtual int DefaultGumpID { get { return ContainerData.GumpID; } }
          public virtual int DefaultDropSound { get { return ContainerData.DropSound; } }
  
          public virtual int DefaultMaxItems { get { return m_GlobalMaxItems; } }
          public virtual int DefaultMaxWeight { get { return m_GlobalMaxWeight; } }
  
          public virtual bool IsDecoContainer { get { return !Movable && !IsLockedDown && !IsSecure && Parent == null && !m_LiftOverride; } }
  
          public virtual int GetDroppedSound(Item item)
          {
              int dropSound = item.GetDropSound();
  
              return dropSound != -1 ? dropSound : DropSound;
          }
  
          public override void OnSnoop(Mobile from)
          {
              if (m_SnoopHandler != null)
              {
                  m_SnoopHandler(this, from);
              }
          }
  
          public override bool CheckLift(Mobile from, Item item, ref LRReason reject)
          {
              if (!from.IsStaff() && IsDecoContainer)
              {
                  reject = LRReason.CannotLift;
                  return false;
              }
  
              return base.CheckLift(from, item, ref reject);
          }
  
          public override bool CheckItemUse(Mobile from, Item item)
          {
              if (item != this && from.AccessLevel < AccessLevel.GameMaster && IsDecoContainer)
              {
                  from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that.
                  return false;
              }
  
              return base.CheckItemUse(from, item);
          }
  
          public bool CheckHold(Mobile m, Item item, bool message)
          {
              return CheckHold(m, item, message, true, 0, 0);
          }
  
          public bool CheckHold(Mobile m, Item item, bool message, bool checkItems)
          {
              return CheckHold(m, item, message, checkItems, 0, 0);
          }
  
          public virtual bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight)
          {
              if (!m.IsStaff())
              {
                  if (IsDecoContainer)
                  {
                      if (message)
                      {
                          SendCantStoreMessage(m, item);
                      }
  
                      return false;
                  }
  
                  int maxItems = MaxItems;
  
                  if (checkItems && maxItems != 0 &&
                      (TotalItems + plusItems + item.TotalItems + (item.IsVirtualItem ? 0 : 1)) > maxItems)
                  {
                      if (message)
                      {
                          SendFullItemsMessage(m, item);
                      }
  
                      return false;
                  }
                  else
                  {
                      int maxWeight = MaxWeight;
  
                      if (maxWeight != 0 && (TotalWeight + plusWeight + item.TotalWeight + item.PileWeight) > maxWeight)
                      {
                          if (message)
                          {
                              SendFullWeightMessage(m, item);
                          }
  
                          return false;
                      }
                  }
              }
  
  			var parent = Parent;
  
              while (parent != null)
              {
                  if (parent is Container)
                  {
                      return ((Container)parent).CheckHold(m, item, message, checkItems, plusItems, plusWeight);
                  }
                  else if (parent is Item)
                  {
                      parent = ((Item)parent).Parent;
                  }
                  else
                  {
                      break;
                  }
              }
  
              return true;
          }
  
          public virtual bool CheckStack(Mobile from, Item item)
          {
              foreach (var i in Items)
              {
                  if (i.WillStack(from, item))
                  {
                      return true;
                  }
              }
  
              return false;
          }
  
          public virtual void SendFullItemsMessage(Mobile to, Item item)
          {
              to.SendMessage("That container cannot hold more items.");
          }
  
          public virtual void SendFullWeightMessage(Mobile to, Item item)
          {
              to.SendMessage("That container cannot hold more weight.");
          }
  
          public virtual void SendCantStoreMessage(Mobile to, Item item)
          {
              to.SendLocalizedMessage(500176); // That is not your container, you can't store things here.
          }
  
          public virtual bool OnDragDropInto(Mobile from, Item item, Point3D p)
          {
              if (!CheckHold(from, item, true, true))
              {
                  return false;
              }
  
              item.Location = new Point3D(p.m_X, p.m_Y, 0);
              AddItem(item);
  
              from.SendSound(GetDroppedSound(item), GetWorldLocation());
  
              return true;
          }
  
          private class GroupComparer : IComparer
          {
              private readonly CheckItemGroup m_Grouper;
  
              public GroupComparer(CheckItemGroup grouper)
              {
                  m_Grouper = grouper;
              }
  
              public int Compare(object x, object y)
              {
                  Item a = (Item)x;
                  Item b = (Item)y;
  
                  return m_Grouper(a, b);
              }
          }
  
          #region Consume[...]
          public bool ConsumeTotalGrouped(Type type, int amount, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
          {
              if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              var typedItems = FindItemsByType(type, recurse);
  
              var groups = new List<List<Item>>();
              int idx = 0;
  
              while (idx < typedItems.Length)
              {
                  Item a = typedItems[idx++];
                  var group = new List<Item>();
  
                  group.Add(a);
  
                  while (idx < typedItems.Length)
                  {
                      Item b = typedItems[idx];
                      int v = grouper(a, b);
  
                      if (v == 0)
                      {
                          group.Add(b);
                      }
                      else
                      {
                          break;
                      }
  
                      ++idx;
                  }
  
                  groups.Add(group);
              }
  
              var items = new Item[groups.Count][];
              var totals = new int[groups.Count];
  
              bool hasEnough = false;
  
              for (int i = 0; i < groups.Count; ++i)
              {
                  items[i] = groups[i].ToArray();
                  //items[i] = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) ));
  
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      totals[i] += items[i][j].Amount;
                  }
  
                  if (totals[i] >= amount)
                  {
                      hasEnough = true;
                  }
              }
  
              if (!hasEnough)
              {
                  return false;
              }
  
              for (int i = 0; i < items.Length; ++i)
              {
                  if (totals[i] >= amount)
                  {
                      int need = amount;
  
                      for (int j = 0; j < items[i].Length; ++j)
                      {
                          Item item = items[i][j];
  
                          int theirAmount = item.Amount;
  
                          if (theirAmount < need)
                          {
                              if (callback != null)
                              {
                                  callback(item, theirAmount);
                              }
  
                              item.Delete();
                              need -= theirAmount;
                          }
                          else
                          {
                              if (callback != null)
                              {
                                  callback(item, need);
                              }
  
                              item.Consume(need);
                              break;
                          }
                      }
  
                      break;
                  }
              }
  
              return true;
          }
  
          public int ConsumeTotalGrouped(
              Type[] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
          {
              if (types.Length != amounts.Length)
              {
                  throw new ArgumentException();
              }
              else if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              var items = new Item[types.Length][][];
              var totals = new int[types.Length][];
  
              for (int i = 0; i < types.Length; ++i)
              {
                  var typedItems = FindItemsByType(types[i], recurse);
  
                  var groups = new List<List<Item>>();
                  int idx = 0;
  
                  while (idx < typedItems.Length)
                  {
                      Item a = typedItems[idx++];
                      var group = new List<Item>();
  
                      group.Add(a);
  
                      while (idx < typedItems.Length)
                      {
                          Item b = typedItems[idx];
                          int v = grouper(a, b);
  
                          if (v == 0)
                          {
                              group.Add(b);
                          }
                          else
                          {
                              break;
                          }
  
                          ++idx;
                      }
                      
                      groups.Add(group);
                  }
  
                  items[i] = new Item[groups.Count][];
                  totals[i] = new int[groups.Count];
  
                  bool hasEnough = false;
  
                  for (int j = 0; j < groups.Count; ++j)
                  {
                      items[i][j] = groups[j].ToArray();
                      //items[i][j] = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
  
                      for (int k = 0; k < items[i][j].Length; ++k)
                      {
                          totals[i][j] += items[i][j][k].Amount;
                      }
  
                      if (totals[i][j] >= amounts[i])
                      {
                          hasEnough = true;
                      }
                  }
  
                  if (!hasEnough)
                  {
                      return i;
                  }
              }
  
              for (int i = 0; i < items.Length; ++i)
              {
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      if (totals[i][j] >= amounts[i])
                      {
                          int need = amounts[i];
  
                          for (int k = 0; k < items[i][j].Length; ++k)
                          {
                              Item item = items[i][j][k];
  
                              int theirAmount = item.Amount;
  
                              if (theirAmount < need)
                              {
                                  if (callback != null)
                                  {
                                      callback(item, theirAmount);
                                  }
  
                                  item.Delete();
                                  need -= theirAmount;
                              }
                              else
                              {
                                  if (callback != null)
                                  {
                                      callback(item, need);
                                  }
  
                                  item.Consume(need);
                                  break;
                              }
                          }
  
                          break;
                      }
                  }
              }
  
              return -1;
          }
  
          public int ConsumeTotalGrouped(
              Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper)
          {
              if (types.Length != amounts.Length)
              {
                  throw new ArgumentException();
              }
              else if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              var items = new Item[types.Length][][];
              var totals = new int[types.Length][];
  
              for (int i = 0; i < types.Length; ++i)
              {
                  var typedItems = FindItemsByType(types[i], recurse);
  
                  var groups = new List<List<Item>>();
                  int idx = 0;
  
                  while (idx < typedItems.Length)
                  {
                      Item a = typedItems[idx++];
                      var group = new List<Item>();
  
                      group.Add(a);
  
                      while (idx < typedItems.Length)
                      {
                          Item b = typedItems[idx];
                          int v = grouper(a, b);
  
                          if (v == 0)
                          {
                              group.Add(b);
                          }
                          else
                          {
                              break;
                          }
  
                          ++idx;
                      }
  
                      groups.Add(group);
                  }
  
                  items[i] = new Item[groups.Count][];
                  totals[i] = new int[groups.Count];
  
                  bool hasEnough = false;
  
                  for (int j = 0; j < groups.Count; ++j)
                  {
                      items[i][j] = groups[j].ToArray();
  
                      for (int k = 0; k < items[i][j].Length; ++k)
                      {
                          totals[i][j] += items[i][j][k].Amount;
                      }
  
                      if (totals[i][j] >= amounts[i])
                      {
                          hasEnough = true;
                      }
                  }
  
                  if (!hasEnough)
                  {
                      return i;
                  }
              }
  
              for (int i = 0; i < items.Length; ++i)
              {
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      if (totals[i][j] >= amounts[i])
                      {
                          int need = amounts[i];
  
                          for (int k = 0; k < items[i][j].Length; ++k)
                          {
                              Item item = items[i][j][k];
  
                              int theirAmount = item.Amount;
  
                              if (theirAmount < need)
                              {
                                  if (callback != null)
                                  {
                                      callback(item, theirAmount);
                                  }
  
                                  item.Delete();
                                  need -= theirAmount;
                              }
                              else
                              {
                                  if (callback != null)
                                  {
                                      callback(item, need);
                                  }
  
                                  item.Consume(need);
                                  break;
                              }
                          }
  
                          break;
                      }
                  }
              }
  
              return -1;
          }
  
          public int ConsumeTotal(Type[][] types, int[] amounts)
          {
              return ConsumeTotal(types, amounts, true, null);
          }
  
          public int ConsumeTotal(Type[][] types, int[] amounts, bool recurse)
          {
              return ConsumeTotal(types, amounts, recurse, null);
          }
  
          public int ConsumeTotal(Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback)
          {
              if (types.Length != amounts.Length)
              {
                  throw new ArgumentException();
              }
  
              var items = new Item[types.Length][];
              var totals = new int[types.Length];
  
              for (int i = 0; i < types.Length; ++i)
              {
                  items[i] = FindItemsByType(types[i], recurse);
  
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      totals[i] += items[i][j].Amount;
                  }
  
                  if (totals[i] < amounts[i])
                  {
                      return i;
                  }
              }
  
              for (int i = 0; i < types.Length; ++i)
              {
                  int need = amounts[i];
  
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      Item item = items[i][j];
  
                      int theirAmount = item.Amount;
  
                      if (theirAmount < need)
                      {
                          if (callback != null)
                          {
                              callback(item, theirAmount);
                          }
  
                          item.Delete();
                          need -= theirAmount;
                      }
                      else
                      {
                          if (callback != null)
                          {
                              callback(item, need);
                          }
  
                          item.Consume(need);
                          break;
                      }
                  }
              }
  
              return -1;
          }
  
          public int ConsumeTotal(Type[] types, int[] amounts)
          {
              return ConsumeTotal(types, amounts, true, null);
          }
  
          public int ConsumeTotal(Type[] types, int[] amounts, bool recurse)
          {
              return ConsumeTotal(types, amounts, recurse, null);
          }
  
          public int ConsumeTotal(Type[] types, int[] amounts, bool recurse, OnItemConsumed callback)
          {
              if (types.Length != amounts.Length)
              {
                  throw new ArgumentException();
              }
  
              var items = new Item[types.Length][];
              var totals = new int[types.Length];
  
              for (int i = 0; i < types.Length; ++i)
              {
                  items[i] = FindItemsByType(types[i], recurse);
  
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      totals[i] += items[i][j].Amount;
                  }
  
                  if (totals[i] < amounts[i])
                  {
                      return i;
                  }
              }
  
              for (int i = 0; i < types.Length; ++i)
              {
                  int need = amounts[i];
  
                  for (int j = 0; j < items[i].Length; ++j)
                  {
                      Item item = items[i][j];
  
                      int theirAmount = item.Amount;
  
                      if (theirAmount < need)
                      {
                          if (callback != null)
                          {
                              callback(item, theirAmount);
                          }
  
                          item.Delete();
                          need -= theirAmount;
                      }
                      else
                      {
                          if (callback != null)
                          {
                              callback(item, need);
                          }
  
                          item.Consume(need);
                          break;
                      }
                  }
              }
  
              return -1;
          }
  
          public bool ConsumeTotal(Type type, int amount)
          {
              return ConsumeTotal(type, amount, true, null);
          }
  
          public bool ConsumeTotal(Type type, int amount, bool recurse)
          {
              return ConsumeTotal(type, amount, recurse, null);
          }
  
          public bool ConsumeTotal(Type type, int amount, bool recurse, OnItemConsumed callback)
          {
              var items = FindItemsByType(type, recurse);
  
              // First pass, compute total
              int total = 0;
  
              for (int i = 0; i < items.Length; ++i)
              {
                  total += items[i].Amount;
              }
  
              if (total >= amount)
              {
                  // We've enough, so consume it
  
                  int need = amount;
  
                  for (int i = 0; i < items.Length; ++i)
                  {
                      Item item = items[i];
  
                      int theirAmount = item.Amount;
  
                      if (theirAmount < need)
                      {
                          if (callback != null)
                          {
                              callback(item, theirAmount);
                          }
  
                          item.Delete();
                          need -= theirAmount;
                      }
                      else
                      {
                          if (callback != null)
                          {
                              callback(item, need);
                          }
  
                          item.Consume(need);
  
                          return true;
                      }
                  }
              }
  
              return false;
          }
  
          public int ConsumeUpTo(Type type, int amount)
          {
              return ConsumeUpTo(type, amount, true);
          }
  
          public int ConsumeUpTo(Type type, int amount, bool recurse)
          {
              int consumed = 0;
  
              var toDelete = new Queue<Item>();
  
              RecurseConsumeUpTo(this, type, amount, recurse, ref consumed, toDelete);
  
              while (toDelete.Count > 0)
              {
                  toDelete.Dequeue().Delete();
              }
  
              return consumed;
          }
  
          private static void RecurseConsumeUpTo(
              Item current, Type type, int amount, bool recurse, ref int consumed, Queue<Item> toDelete)
          {
              if (current != null && current.Items.Count > 0)
              {
                  var list = current.Items;
  
                  for (int i = 0; i < list.Count; ++i)
                  {
                      Item item = list[i];
  
                      if (type.IsAssignableFrom(item.GetType()))
                      {
                          int need = amount - consumed;
                          int theirAmount = item.Amount;
  
                          if (theirAmount <= need)
                          {
                              toDelete.Enqueue(item);
                              consumed += theirAmount;
                          }
                          else
                          {
                              item.Amount -= need;
                              consumed += need;
  
                              return;
                          }
                      }
                      else if (recurse && item is Container)
                      {
                          RecurseConsumeUpTo(item, type, amount, recurse, ref consumed, toDelete);
                      }
                  }
              }
          }
          #endregion
  
          #region Get[BestGroup]Amount
          public int GetBestGroupAmount(Type type, bool recurse, CheckItemGroup grouper)
          {
              if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              int best = 0;
  
              var typedItems = FindItemsByType(type, recurse);
  
              var groups = new List<List<Item>>();
              int idx = 0;
  
              while (idx < typedItems.Length)
              {
                  Item a = typedItems[idx++];
                  var group = new List<Item>();
  
                  group.Add(a);
  
                  while (idx < typedItems.Length)
                  {
                      Item b = typedItems[idx];
                      int v = grouper(a, b);
  
                      if (v == 0)
                      {
                          group.Add(b);
                      }
                      else
                      {
                          break;
                      }
  
                      ++idx;
                  }
  
                  groups.Add(group);
              }
  
              for (int i = 0; i < groups.Count; ++i)
              {
                  var items = groups[i].ToArray();
  
                  //Item[] items = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) ));
                  int total = 0;
  
                  for (int j = 0; j < items.Length; ++j)
                  {
                      total += items[j].Amount;
                  }
  
                  if (total >= best)
                  {
                      best = total;
                  }
              }
  
              return best;
          }
  
          public int GetBestGroupAmount(Type[] types, bool recurse, CheckItemGroup grouper)
          {
              if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              int best = 0;
  
              var typedItems = FindItemsByType(types, recurse);
  
              var groups = new List<List<Item>>();
              int idx = 0;
  
              while (idx < typedItems.Length)
              {
                  Item a = typedItems[idx++];
                  var group = new List<Item>();
  
                  group.Add(a);
  
                  while (idx < typedItems.Length)
                  {
                      Item b = typedItems[idx];
                      int v = grouper(a, b);
  
                      if (v == 0)
                      {
                          group.Add(b);
                      }
                      else
                      {
                          break;
                      }
  
                      ++idx;
                  }
  
                  groups.Add(group);
              }
  
              for (int j = 0; j < groups.Count; ++j)
              {
                  var items = groups[j].ToArray();
                  //Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
                  int total = 0;
  
                  for (int k = 0; k < items.Length; ++k)
                  {
                      total += items[k].Amount;
                  }
  
                  if (total >= best)
                  {
                      best = total;
                  }
              }
  
              return best;
          }
  
          public int GetBestGroupAmount(Type[][] types, bool recurse, CheckItemGroup grouper)
          {
              if (grouper == null)
              {
                  throw new ArgumentNullException();
              }
  
              int best = 0;
  
              for (int i = 0; i < types.Length; ++i)
              {
                  var typedItems = FindItemsByType(types[i], recurse);
  
                  var groups = new List<List<Item>>();
                  int idx = 0;
  
                  while (idx < typedItems.Length)
                  {
                      Item a = typedItems[idx++];
                      var group = new List<Item>();
  
                      group.Add(a);
  
                      while (idx < typedItems.Length)
                      {
                          Item b = typedItems[idx];
                          int v = grouper(a, b);
  
                          if (v == 0)
                          {
                              group.Add(b);
                          }
                          else
                          {
                              break;
                          }
  
                          ++idx;
                      }
  
                      groups.Add(group);
                  }
  
                  for (int j = 0; j < groups.Count; ++j)
                  {
                      var items = groups[j].ToArray();
                      //Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) ));
                      int total = 0;
  
                      for (int k = 0; k < items.Length; ++k)
                      {
                          total += items[k].Amount;
                      }
  
                      if (total >= best)
                      {
                          best = total;
                      }
                  }
              }
  
              return best;
          }
  
          public int GetAmount(Type type)
          {
              return GetAmount(type, true);
          }
  
          public int GetAmount(Type type, bool recurse)
          {
              var items = FindItemsByType(type, recurse);
  
              int amount = 0;
  
              for (int i = 0; i < items.Length; ++i)
              {
                  amount += items[i].Amount;
              }
  
              return amount;
          }
  
          public int GetAmount(Type[] types)
          {
              return GetAmount(types, true);
          }
  
          public int GetAmount(Type[] types, bool recurse)
          {
              var items = FindItemsByType(types, recurse);
  
              int amount = 0;
  
              for (int i = 0; i < items.Length; ++i)
              {
                  amount += items[i].Amount;
              }
  
              return amount;
          }
          #endregion
  
          private static readonly List<Item> m_FindItemsList = new List<Item>();
  
          #region Non-Generic FindItem[s] by Type
          public Item[] FindItemsByType(Type type)
          {
              return FindItemsByType(type, true);
          }
  
          public Item[] FindItemsByType(Type type, bool recurse)
          {
              if (m_FindItemsList.Count > 0)
              {
                  m_FindItemsList.Clear();
              }
  
              RecurseFindItemsByType(this, type, recurse, m_FindItemsList);
  
              return m_FindItemsList.ToArray();
          }
  
          private static void RecurseFindItemsByType(Item current, Type type, bool recurse, List<Item> list)
          {
              if (current != null && current.Items.Count > 0)
              {
                  var items = current.Items;
  
                  for (int i = 0; i < items.Count; ++i)
                  {
                      Item item = items[i];
  
                      if (type.IsAssignableFrom(item.GetType())) // item.GetType().IsAssignableFrom( type ) )
                      {
                          list.Add(item);
                      }
  
                      if (recurse && item is Container)
                      {
                          RecurseFindItemsByType(item, type, recurse, list);
                      }
                  }
              }
          }
  
          public Item[] FindItemsByType(Type[] types)
          {
              return FindItemsByType(types, true);
          }
  
          public Item[] FindItemsByType(Type[] types, bool recurse)
          {
              if (m_FindItemsList.Count > 0)
              {
                  m_FindItemsList.Clear();
              }
  
              RecurseFindItemsByType(this, types, recurse, m_FindItemsList);
  
              return m_FindItemsList.ToArray();
          }
  
          private static void RecurseFindItemsByType(Item current, Type[] types, bool recurse, List<Item> list)
          {
              if (current != null && current.Items.Count > 0)
              {
                  var items = current.Items;
  
                  for (int i = 0; i < items.Count; ++i)
                  {
                      Item item = items[i];
  
                      if (InTypeList(item, types))
                      {
                          list.Add(item);
                      }
  
                      if (recurse && item is Container)
                      {
                          RecurseFindItemsByType(item, types, recurse, list);
                      }
                  }
              }
          }
  
          public Item FindItemByType(Type type)
          {
              return FindItemByType(type, true);
          }
  
          public Item FindItemByType(Type type, bool recurse)
          {
              return RecurseFindItemByType(this, type, recurse);
          }
  
          private static Item RecurseFindItemByType(Item current, Type type, bool recurse)
          {
              if (current != null && current.Items.Count > 0)
              {
                  var list = current.Items;
  
                  for (int i = 0; i < list.Count; ++i)
                  {
                      Item item = list[i];
  
                      if (type.IsAssignableFrom(item.GetType()))
                      {
                          return item;
                      }
                      else if (recurse && item is Container)
                      {
                          Item check = RecurseFindItemByType(item, type, recurse);
  
                          if (check != null)
                          {
                              return check;
                          }
                      }
                  }
              }
  
              return null;
          }
  
          public Item FindItemByType(Type[] types)
          {
              return FindItemByType(types, true);
          }
  
          public Item FindItemByType(Type[] types, bool recurse)
          {
              return RecurseFindItemByType(this, types, recurse);
          }
  
          private static Item RecurseFindItemByType(Item current, Type[] types, bool recurse)
          {
              if (current != null && current.Items.Count > 0)
              {
                  var list = current.Items;
  
                  for (int i = 0; i < list.Count; ++i)
                  {
                      Item item = list[i];
  
                      if (InTypeList(item, types))
                      {
                          return item;
                      }
                      else if (recurse && item is Container)
                      {
                          Item check = RecurseFindItemByType(item, types, recurse);
  
                          if (check != null)
                          {
                              return check;
                          }
                      }
                  }
              }
  
              return null;
          }
          #endregion
  
          #region Generic FindItem[s] by Type
          public List<T> FindItemsByType<T>() where T : Item
          {
              return FindItemsByType<T>(true, null);
          }
  
          public List<T> FindItemsByType<T>(bool recurse) where T : Item
          {
              return FindItemsByType<T>(recurse, null);
          }
  
          public List<T> FindItemsByType<T>(Predicate<T> predicate) where T : Item
          {
              return FindItemsByType(true, predicate);
          }
  
          public List<T> FindItemsByType<T>(bool recurse, Predicate<T> predicate) where T : Item
          {
              if (m_FindItemsList.Count > 0)
              {
                  m_FindItemsList.Clear();
              }
  
              var list = new List<T>();
  
              RecurseFindItemsByType(this, recurse, list, predicate);
  
              return list;
          }
  
          private static void RecurseFindItemsByType<T>(Item current, bool recurse, List<T> list, Predicate<T> predicate)
              where T : Item
          {
              if (current != null && current.Items.Count > 0)
              {
                  var items = current.Items;
  
                  for (int i = 0; i < items.Count; ++i)
                  {
                      Item item = items[i];
  
                      if (typeof(T).IsAssignableFrom(item.GetType()))
                      {
                          T typedItem = (T)item;
  
                          if (predicate == null || predicate(typedItem))
                          {
                              list.Add(typedItem);
                          }
                      }
  
                      if (recurse && item is Container)
                      {
                          RecurseFindItemsByType(item, recurse, list, predicate);
                      }
                  }
              }
          }
  
          public T FindItemByType<T>() where T : Item
          {
              return FindItemByType<T>(true);
          }
  
          public T FindItemByType<T>(Predicate<T> predicate) where T : Item
          {
              return FindItemByType(true, predicate);
          }
  
          public T FindItemByType<T>(bool recurse) where T : Item
          {
              return FindItemByType<T>(recurse, null);
          }
  
          public T FindItemByType<T>(bool recurse, Predicate<T> predicate) where T : Item
          {
              return RecurseFindItemByType(this, recurse, predicate);
          }
  
          private static T RecurseFindItemByType<T>(Item current, bool recurse, Predicate<T> predicate) where T : Item
          {
  
gt:             if (current != null && current.Items.Count > 0)