public bool SquashDuplicateSlotChanges()
        {
            Dictionary <int, List <SlotChangeAction> > slotChanges = new Dictionary <int, List <SlotChangeAction> >();

            for (int i = 0; i < this.Actions.Count; ++i)
            {
                if (this.Actions[i] is SlotChangeAction)
                {
                    SlotChangeAction action = (SlotChangeAction)this.Actions[i];
                    int hash = action.Inventory.GetHashCode() | action.InventorySlot;
                    List <SlotChangeAction> list;
                    if (slotChanges.ContainsKey(hash))
                    {
                        list = slotChanges[hash];
                    }
                    else
                    {
                        list = new List <SlotChangeAction>();
                    }
                    list.Add(action);
                    slotChanges[hash] = list;
                }
            }

            KeyValuePair <int, List <SlotChangeAction> >[] entries = slotChanges.ToArray();
            for (int c = 0; c < entries.Length; ++c)
            {
                int hash = entries[c].Key;
                List <SlotChangeAction> list = entries[c].Value;

                if (list.Count == 1)
                {
                    slotChanges.Remove(hash);
                    continue;
                }

                List <SlotChangeAction> originalList = new List <SlotChangeAction>(list);

                SlotChangeAction originalAction = null;
                Item             lastTargetItem = null;

                for (int i = 0; i < list.Count; ++i)
                {
                    SlotChangeAction action = list[i];
                    if (action.IsValid(this.Player))
                    {
                        originalAction = action;
                        lastTargetItem = action.TargetItem;
                        list.RemoveAt(i);
                        break;
                    }
                }
                if (originalAction == null)
                {
                    return(false);
                }

                int sortedThisLoop;

                do
                {
                    sortedThisLoop = 0;
                    for (int i = 0; i < list.Count; ++i)
                    {
                        SlotChangeAction action = list[i];

                        Item actionSource = action.SourceItem;
                        if (actionSource == lastTargetItem)
                        {
                            lastTargetItem = action.TargetItem;
                            list.RemoveAt(i);
                            sortedThisLoop++;
                        }
                        else if (actionSource.Equals(lastTargetItem, true, false, true, true))
                        {
                            lastTargetItem.Count -= actionSource.Count;
                            list.RemoveAt(i);
                            if (lastTargetItem.Count == 0)
                            {
                                sortedThisLoop++;
                            }
                        }
                    }
                } while (sortedThisLoop > 0);

                if (list.Count > 0)
                {
                    return(false);
                }

                for (int i = 0; i < originalList.Count; ++i)
                {
                    this.Actions.Remove(originalList[i]);
                }
                this.AddAction(new SlotChangeAction(originalAction.Inventory, originalAction.InventorySlot, originalAction.SourceItem, lastTargetItem));
            }
            return(true);
        }
Пример #2
0
        /// <inheritdoc />
        protected override void HandleNormalTransaction(NormalTransaction normal)
        {
            ObservableCollection <InventoryAction> actions = new ObservableCollection <InventoryAction>();

            actions.CollectionChanged += (sender, args) =>
            {
                if (args.Action == NotifyCollectionChangedAction.Add)
                {
                    foreach (InventoryAction add in args.NewItems)
                    {
                        add.OnAddToTransaction();
                    }
                }
            };

            foreach (var transaction in normal.TransactionRecords)
            {
                var newItem = transaction.NewItem;
                var oldItem = transaction.OldItem;

                if (SlotChangeAction.EqualsExactly(newItem, oldItem))
                {
                    continue;
                }

                switch (transaction)
                {
                case WorldInteractionTransactionRecord wit:
                {
                    if (wit.Slot != 0)
                    {
                        Log.Warn($"Got non item-drop in WorldInteractionTransactionRecord!");
                        InventoryMisMatch();

                        break;
                    }

                    actions.Add(new DropItemAction(newItem));

                    bool didMatch = false;

                    foreach (var record in normal.RequestRecords)
                    {
                        foreach (var slot in record.Slots)
                        {
                            var item = GetContainerItem(record.ContainerId, slot);

                            if (item.Id == wit.NewItem.Id && item.Metadata == wit.NewItem.Metadata &&
                                item.Count >= wit.NewItem.Count)
                            {
                                item.Count -= newItem.Count;

                                if (DropItem(newItem, item))
                                {
                                    DropItem(wit.NewItem);
                                    didMatch = true;

                                    break;
                                }
                                else
                                {
                                    item.Count += wit.NewItem.Count;
                                }
                            }
                        }

                        if (didMatch)
                        {
                            break;
                        }
                    }

                    if (normal.RequestRecords.Count > 0 && !didMatch)
                    {
                        Log.Warn($"WorldInteractionTransactionRecord: No matching item found.");
                        InventoryMisMatch();

                        return;
                    }

                    // Log.Info($"WorldInteractionTransactionRecord: (Flags={wit.Flags} Slot={wit.Slot} NewItem={wit.NewItem} OldItem={wit.OldItem} StackId={wit.StackNetworkId})");
                } break;

                case ContainerTransactionRecord ctr:
                {
                    //  var item = GetInvItem(ctr.InventoryId, ctr.Slot);

                    /*
                     * if (item.Count != newItem.Count)
                     * {
                     *      Log.Warn($"ContainerTransactionRecord invalid! Expected: {item.Count} Got: {newItem.Count} (OldItem={oldItem})");
                     *      InventoryMisMatch();
                     *
                     *      return;
                     * }
                     */

                    actions.Add(new SlotChangeAction(this, ctr.InventoryId, ctr.Slot, oldItem, newItem));
                    //Log.Info($"ContainerTransactionRecord (InventoryId={ctr.InventoryId} Slot={ctr.Slot} StackId={ctr.StackNetworkId}) (NewItem={ctr.NewItem}) (OldItem={ctr.OldItem})");
                } break;
                }
            }

            foreach (var action in actions)
            {
                if (!action.IsValid(this))
                {
                    Log.Info($"Invalid action: {action}");
                    break;
                }
            }

            foreach (var action in actions)
            {
                if (action.PreExecute(this))
                {
                    if (action.Execute(this))
                    {
                        action.ExecutionSucceeded(this);
                    }
                    else
                    {
                        action.ExecutionFailed(this);
                    }
                }
            }
        }