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);
        }
Exemple #2
0
        public override Result <int> GetCanAddAmount(TElementType item, int bailEarly, CollectionContext context)
        {
            if (isReadOnly)
            {
                return(new Result <int>(0, Errors.CollectionIsReadOnly));
            }

            var canAddRestriction = CheckAddRestriction(item, context);

            if (canAddRestriction.result == false)
            {
                return(new Result <int>(0, canAddRestriction.error));
            }

            // Avoid chaning the caller's context.
            context = context.Clone();
            context.allowAABBCollisionSelf = false;
            context.validationFlags       &= ~(CollectionContext.Validations.Restrictions | CollectionContext.Validations.SpecificInstance);

            // Keep track of which slots get 'occupied' when we set our item in a new slot (The item might occupy multiple slots).
            int addAmount    = 0;
            var occupiedList = new BitArray(slotCount);

            for (int i = 0; i < slotCount; i++)
            {
                if (CanSet(i, item, item.maxStackSize, context).result)
                {
                    if (IsNull(slots[i].item) && occupiedList[i] == false && CanSetAABB(i, item, 1, context))
                    {
                        for (int x = 0; x < item.layoutShape.convexX; x++)
                        {
                            for (int y = 0; y < item.layoutShape.convexY; y++)
                            {
                                occupiedList[i + Offset(x, y)] = true;
                            }
                        }

                        addAmount += item.maxStackSize;
                    }
                    else if (AreEqual(slots[i].item, item) && occupiedList[i] == false)
                    {
                        var canAdd = item.maxStackSize - slots[i].amount;
                        addAmount += canAdd;
                    }
                }

                if (addAmount >= bailEarly)
                {
                    break;
                }
            }

            return(addAmount);
        }
        public virtual Result <int> GetCanAddAmount(TElementType item, int bailEarly, CollectionContext context)
        {
            if (isReadOnly)
            {
                return(new Result <int>(0, Errors.CollectionIsReadOnly));
            }

            // Handle restrictions here once to avoid doing it on every CanSet call
            var canAdd = CheckAddRestriction(item, context);

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

            // Avoid chaning the caller's context.
            context = context.Clone();
            context.validationFlags &= ~(CollectionContext.Validations.Restrictions | CollectionContext.Validations.SpecificInstance);

            int totalAmount = 0;

            for (int i = 0; i < slots.Length; i++)
            {
                if (CanSet(i, item, item.maxStackSize, context).result)
                {
                    if (IsNull(slots[i].item))
                    {
                        totalAmount += item.maxStackSize;
                    }
                    else if (AreEqual(slots[i].item, item))
                    {
                        totalAmount += item.maxStackSize - slots[i].amount;
                    }
                }

                if (totalAmount >= bailEarly)
                {
                    break;
                }
            }

            return(new Result <int>(totalAmount));
        }
        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);
        }