/// <summary> /// Immediately attempts to perform an item movement between two containers. /// </summary> /// <param name="context">A reference to the operation context.</param> /// <param name="item">The item being moved.</param> /// <param name="fromContainer">The container from which the movement is being performed.</param> /// <param name="toContainer">The container to which the movement is being performed.</param> /// <param name="fromIndex">Optional. The index within the container to move the item from.</param> /// <param name="toIndex">Optional. The index within the container to move the item to.</param> /// <param name="amountToMove">Optional. The amount of the thing to move. Defaults to 1.</param> /// <param name="requestorCreature">Optional. The creature that this movement is being performed in behalf of, if any.</param> /// <returns>True if the movement was successfully performed, false otherwise.</returns> /// <remarks>Changes game state, should only be performed after all pertinent validations happen.</remarks> private bool PerformItemMovement(IOperationContext context, IItem item, IItemsContainer fromContainer, IItemsContainer toContainer, byte fromIndex = byte.MaxValue, byte toIndex = byte.MaxValue, byte amountToMove = 1, ICreature requestorCreature = null) { const byte FallbackIndex = byte.MaxValue; if (item == null || fromContainer == null || toContainer == null) { return(false); } var sameContainer = fromContainer == toContainer; if (sameContainer && fromIndex == toIndex) { // no change at all. return(true); } // Edge case, check if the moving item is the target container. if (item is IContainerItem containerItem && toContainer is IContainerItem targetContainer && targetContainer.IsDescendantOf(containerItem)) { return(false); } (bool removeSuccessful, IThing removeRemainder) = fromContainer.RemoveItem(context.ItemFactory, ref item, amountToMove, fromIndex); if (!removeSuccessful) { // Failing to remove the item from the original container stops the entire operation. return(false); } if (fromContainer is ITile fromTile) { this.SendNotification(context, new TileUpdatedNotification(context.Map.FindPlayersThatCanSee(fromTile.Location), fromTile)); } /* context.EventRulesApi.EvaluateRules(this, EventRuleType.Separation, new SeparationEventRuleArguments(fromThingContainer.Location, item, requestorCreature)); */ IItem remainder = item; if (sameContainer && removeRemainder == null && fromIndex < toIndex) { // If the move happens within the same container, we need to adjust the index of where we're adding, depending if it is before or after. toIndex--; } if (!toContainer.AddItemToContainerRecursively(context.ItemFactory, ref remainder, toIndex, includeTileAsFallback: false) || remainder != null) { // There is some rollback to do, as we failed to add the entire thing. IItem rollbackRemainder = remainder ?? item; if (!fromContainer.AddItemToContainerRecursively(context.ItemFactory, ref rollbackRemainder, FallbackIndex, includeTileAsFallback: true)) { context.Logger.LogError($"Rollback failed on {nameof(this.PerformItemMovement)}. Thing: {rollbackRemainder.DescribeForLogger()}"); } } return(true); }