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); }
protected void InvokeOnSlotsChanged(CollectionSlotsChangedResult slotsChangedResult) { OnSlotsChanged?.Invoke(this, slotsChangedResult); }