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 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); }