protected Result <bool> Set2Collections(ICollection fromCol, int fromIndex, ICollection toCol, int toIndex, object fromItem, int fromAmount, object toItem, int toAmount, CollectionContext context)
        {
            // Create a clone to avoid ovewriting settings
            context = context.Clone();
            var fireEventsBefore = context.HasFlag(CollectionContext.Events.SlotChanged);

            if (fromCol == toCol)
            {
                // Staying in the same collection, avoid firing add / remove events
                context.fireEventFlags   = 0;
                context.validationFlags &= ~CollectionContext.Validations.SpecificInstance;
            }

            var can = CanSet2Collections(fromCol, fromIndex, toCol, toIndex, fromItem, fromAmount, toItem, toAmount, context);

            if (can.result == false)
            {
                return(can);
            }

            context.originalIndex = toIndex;
            fromCol.SetBoxed(fromIndex, fromItem, fromAmount, context);
            context.originalIndex = fromIndex;
            toCol.SetBoxed(toIndex, toItem, toAmount, context);

            if (fireEventsBefore && fromCol == toCol)
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(new int[] { fromIndex, toIndex }));
            }

            return(true);
        }
        public virtual Result <CollectionRemoveResult> Remove(TElementType item, int amount, CollectionContext context)
        {
            var canRemove = CanRemove(item, amount, context);

            if (canRemove.result == false)
            {
                return(new Result <CollectionRemoveResult>(null, canRemove.error));
            }

            var indices        = IndexOfAll(o => ReferenceEquals(o, item));
            var affectedSlots  = new List <SlotAmount>();
            var amountToRemove = amount;

            amountToRemove = RemoveFromIndices(indices, amountToRemove, affectedSlots, context);

            if (amountToRemove > 0)
            {
                indices        = IndexOfAll(item);
                amountToRemove = RemoveFromIndices(indices, amountToRemove, affectedSlots, context);
            }

            var response = new Result <CollectionRemoveResult>(new CollectionRemoveResult(affectedSlots.ToArray()));

            if (context.HasFlag(CollectionContext.Events.Remove))
            {
                OnRemovedItem?.Invoke(this, response.result);
            }

            if (context.HasFlag(CollectionContext.Events.SlotChanged))
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(response.result.affectedSlots.Select(o => o.slot).ToArray()));
            }

            return(response);
        }
        public virtual void GenerateSlotsRange(Type slotType, int startIndex, int endIndex, CollectionContext context)
        {
            if (slotType.IsSubclassOf(typeof(TSlotType)) == false && typeof(TSlotType) != slotType)
            {
                throw new ArgumentException($"Given type {slotType.Name} is not a subclass of {typeof(TSlotType).Name}");
            }

            if (isReadOnly)
            {
                return;
            }

            int[] changedSlots = new int[endIndex - startIndex + 1];
            for (int i = startIndex; i <= endIndex; i++)
            {
                var inst = (TSlotType)Activator.CreateInstance(slotType, new object[0]);
                inst.collection = this;
                inst.index      = i;

                slots[i] = inst;
                changedSlots[i - startIndex] = i;
            }

            if (context.HasFlag(CollectionContext.Events.SlotChanged))
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(changedSlots));
            }
        }
        public virtual Result <CollectionRemoveResult> Remove(TElementType item, int amount, CollectionContext context)
        {
            var canRemove = CanRemove(item, amount, context);

            if (canRemove.result == false)
            {
                return(new Result <CollectionRemoveResult>(null, canRemove.error));
            }

            var indices        = IndexOfAll(item);
            var affectedSlots  = new List <SlotAmount>();
            var amountToRemove = amount;

            foreach (int index in indices)
            {
                if (amountToRemove - slots[index].amount < 0)
                {
                    // Just remove moveAmount, there's enough in this slot to complete
                    SetInternal(index, slots[index].item, slots[index].amount - amountToRemove, context);
                    affectedSlots.Add(new SlotAmount(index, amountToRemove));
                    amountToRemove = 0;
                    break;
                }

                affectedSlots.Add(new SlotAmount(index, slots[index].amount));
                amountToRemove -= slots[index].amount;
                SetInternal(index, default(TElementType), 0, context);
            }

            var response = new Result <CollectionRemoveResult>(new CollectionRemoveResult(affectedSlots.ToArray()));

            if (context.HasFlag(CollectionContext.Events.Remove))
            {
                OnRemovedItem?.Invoke(this, response.result);
            }

            if (context.HasFlag(CollectionContext.Events.SlotChanged))
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(response.result.affectedSlots.Select(o => o.slot).ToArray()));
            }

            return(response);
        }
        public void Clear(CollectionContext context)
        {
            if (isReadOnly)
            {
                return;
            }

            var l            = new List <SlotAmount>();
            int removedCount = 0;

            for (var i = 0; i < slots.Length; i++)
            {
                var slot = slots[i];
                if (slot.isOccupied)
                {
                    l.Add(new SlotAmount(i, GetAmount(i)));
                    removedCount += slot.amount;
                    slot.Clear();
                }
            }

            if (l.Count > 0)
            {
                var affectedSlots = l.ToArray();

                if (context.HasFlag(CollectionContext.Events.Remove))
                {
                    OnRemovedItem?.Invoke(this, new CollectionRemoveResult(affectedSlots));
                }

                if (context.HasFlag(CollectionContext.Events.SlotChanged))
                {
                    OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(affectedSlots.Select(o => o.slot).ToArray()));
                }
            }
        }
        public virtual Result <bool> Set(int index, TElementType item, int amount, CollectionContext context)
        {
            var canSet = CanSet(index, item, amount, context);

            if (canSet.result == false)
            {
                return(canSet);
            }

            // Slot is empty and item is empty, ignore call
            if (slots[index].isOccupied == false && IsNull(item))
            {
                return(true);
            }

            if (AreEqual(slots[index].item, item) && GetAmount(index) == amount)
            {
                // Still set for *SpecificInstance
                SetInternal(index, item, amount, context);
                return(true);
            }

            var currentAmount = GetAmount(index);

            if (AreEqual(slots[index].item, item))
            {
                SetInternal(index, item, amount, context);

                var diff = Math.Abs(amount - currentAmount);
                if (amount < currentAmount)
                {
                    if (context.HasFlag(CollectionContext.Events.Remove))
                    {
                        OnRemovedItem?.Invoke(this, new CollectionRemoveResult(new SlotAmount[] { new SlotAmount(index, diff) }));
                    }
                }
                else if (amount > currentAmount)
                {
                    if (context.HasFlag(CollectionContext.Events.Add))
                    {
                        OnAddedItem?.Invoke(this, new CollectionAddResult(new SlotAmount[] { new SlotAmount(index, diff) }));
                    }
                }
            }
            else
            {
                if (IsNull(item))
                {
                    SetInternal(index, item, 0, context);
                    if (context.HasFlag(CollectionContext.Events.Remove))
                    {
                        OnRemovedItem?.Invoke(this, new CollectionRemoveResult(new [] { new SlotAmount(index, currentAmount) }));
                    }
                }
                else
                {
                    if (IsNull(slots[index].item) == false)
                    {
                        SetInternal(index, default(TElementType), 0, context);
                        if (context.HasFlag(CollectionContext.Events.Remove))
                        {
                            OnRemovedItem?.Invoke(this, new CollectionRemoveResult(new [] { new SlotAmount(index, currentAmount) }));
                        }
                    }

                    // Add the item
                    SetInternal(index, item, amount, context);
                    if (context.HasFlag(CollectionContext.Events.Add))
                    {
                        OnAddedItem?.Invoke(this, new CollectionAddResult(new [] { new SlotAmount(index, amount) }));
                    }
                }
            }

            if (context.HasFlag(CollectionContext.Events.SlotChanged))
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(new int[] { index }));
            }

            return(true);
        }
        public virtual Result <CollectionAddResult> Add(TElementType item, int amount, CollectionContext context)
        {
            var canAdd = CanAdd(item, amount, context);

            if (canAdd.result == false)
            {
                return(new Result <CollectionAddResult>(null, canAdd.error));
            }

            var totalAddAmount = amount;
            var affectedSlots  = new List <SlotAmount>();

            // Can Add already checks all restrictions and requirements.
            var contextClone = context.Clone();

            contextClone.validationFlags &= ~CollectionContext.Validations.Restrictions;
            contextClone.validationFlags &= ~CollectionContext.Validations.SpecificInstance;

            // We'll handle events ourselves and bundle them
            contextClone.fireEventFlags = 0;

            var enumerator = GetAddItemEnumerator(item, amount, contextClone);

            while (enumerator.MoveNext())
            {
                var index = enumerator.Current;

                // Slot is occupied by other slot.
                if (slots[index].isOccupied && IsNull(slots[index].item))
                {
                    continue;
                }

                var isEmptySlot         = this[index] == null;
                var canAddToStackAmount = Math.Min(item.maxStackSize - GetAmount(index), totalAddAmount);
                totalAddAmount -= canAddToStackAmount;
                affectedSlots.Add(new SlotAmount(index, canAddToStackAmount));

                if (totalAddAmount > 0)
                {
                    // Need to do another stack placement after this one.
                    if (isEmptySlot)
                    {
                        // Empty slot, so set the reference (and make a clone later if we need to place more).
                        SetInternal(index, item, GetAmount(index) + canAddToStackAmount, contextClone);
                    }
                    else
                    {
                        // If we're adding to an existing stack and still have to place more keep the existing item in the slot and just increase the amount.
                        SetInternal(index, this[index], GetAmount(index) + canAddToStackAmount, contextClone);
                    }
                }
                else
                {
                    // We don't want to place any more after this iteration.
                    SetInternal(index, item, GetAmount(index) + canAddToStackAmount, contextClone);
                }

                if (totalAddAmount <= 0)
                {
                    break;
                }

                if (isEmptySlot)
                {
                    item = CreateElementClone(item); // The next item we're placing needs to be a new instance.
                }
            }

            logger.LogVerbose($"Added {item} x {amount} to collection", this);
            var response = new Result <CollectionAddResult>(new CollectionAddResult(affectedSlots.ToArray()));

            if (context.HasFlag(CollectionContext.Events.Add))
            {
                OnAddedItem?.Invoke(this, response.result);
            }

            if (context.HasFlag(CollectionContext.Events.SlotChanged))
            {
                OnSlotsChanged?.Invoke(this, new CollectionSlotsChangedResult(response.result.affectedSlots.Select(o => o.slot).ToArray()));
            }

            return(response);
        }
Exemple #8
0
 protected void InvokeOnSlotsChanged(CollectionSlotsChangedResult slotsChangedResult)
 {
     OnSlotsChanged?.Invoke(this, slotsChangedResult);
 }