public ITrackedStack GetOutput()
        {
            ITrackedStack overrideOutput = OverridingMachine.GetOutput();

            if (overrideOutput != null)
            {
                return(overrideOutput);
            }
            else
            {
                MassProductionMachineDefinition mpm = ModEntry.GetMPMMachine(OriginalMachineObject.name, OriginalMachineObject.GetMassProducerKey());

                if (mpm != null)
                {
                    // Check for special overriding logic, if any is required
                    string originalClassName = OriginalMachine.GetType().FullName;

                    if (VanillaOverrideList.GetFor(originalClassName) != null)
                    {
                        IVanillaOverride vanillaOverride = VanillaOverrideList.GetFor(originalClassName);
                        overrideOutput = vanillaOverride.Automate_GetOutput(mpm, OriginalMachine, OriginalMachineObject);

                        return(overrideOutput);
                    }
                    else
                    {
                        return(null);
                    }
                }
                else
                {
                    return(OriginalMachine.GetOutput());
                }
            }
        }
Esempio n. 2
0
        /// <summary>Returns an enumerator that iterates through the collection.</summary>
        /// <returns>An enumerator that can be used to iterate through the collection.</returns>
        public IEnumerator <ITrackedStack> GetEnumerator()
        {
            foreach (Item item in this.Chest.items.ToArray())
            {
                if (item != null)
                {
                    ITrackedStack stack = null;
                    try
                    {
                        stack = this.GetTrackedItem(item);
                    }
                    catch (Exception ex)
                    {
                        string error = $"Failed to retrieve item #{item.ParentSheetIndex} ('{item.Name}'";
                        if (item is SObject obj && obj.preservedParentSheetIndex.Value >= 0)
                        {
                            error += $", preserved item #{obj.preservedParentSheetIndex.Value}";
                        }
                        error += $") from container '{this.Chest.Name}' at {this.Location.Name} (tile: {this.TileArea.X}, {this.TileArea.Y}).";

                        throw new InvalidOperationException(error, ex);
                    }

                    if (stack != null)
                    {
                        yield return(stack);
                    }
                }
            }
        }
Esempio n. 3
0
        /// <summary>Get an ingredient needed for a recipe.</summary>
        /// <param name="recipes">The items to match.</param>
        /// <param name="consumable">The matching consumables.</param>
        /// <param name="recipe">The matched requisition.</param>
        /// <returns>Returns whether the requirement is met.</returns>
        public bool TryGetIngredient(IRecipe[] recipes, out IConsumable consumable, out IRecipe recipe)
        {
            IDictionary <IRecipe, StackAccumulator> accumulator = recipes.ToDictionary(req => req, req => new StackAccumulator());

            foreach (ITrackedStack input in this.GetItems())
            {
                foreach (var entry in accumulator)
                {
                    recipe = entry.Key;
                    StackAccumulator stacks = entry.Value;

                    if (recipe.AcceptsInput(input))
                    {
                        ITrackedStack stack = stacks.Add(input);
                        if (stack.Count >= recipe.InputCount)
                        {
                            consumable = new Consumable(stack, entry.Key.InputCount);
                            return(true);
                        }
                    }
                }
            }

            consumable = null;
            recipe     = null;
            return(false);
        }
        /// <summary>Provide input to the machine.</summary>
        /// <param name="input">The available items.</param>
        /// <returns>Returns whether the machine started processing an item.</returns>
        public override bool SetInput(IStorage input)
        {
            // get next item
            ITrackedStack tracker = input.GetItems().FirstOrDefault(p => p.Sample is SObject obj && obj.canBeShipped());

            if (tracker == null)
            {
                return(false);
            }

            // ship item
            SObject item    = (SObject)tracker.Take(tracker.Count);
            var     binList = (this.Location as Farm ?? Game1.getFarm()).getShippingBin(Game1.MasterPlayer);

            Utility.addItemToThisInventoryList(item, binList, listMaxSpace: int.MaxValue);

            // play animation/sound
            if (this.Bin != null)
            {
                this.Bin.showShipment(item, false);
            }
            else if (this.Location is IslandWest islandFarm)
            {
                islandFarm.showShipment(item, false);
            }
            else if (this.Location is Farm farm)
            {
                farm.showShipment(item, false);
            }

            return(true);
        }
Esempio n. 5
0
 /*********
 ** Private methods
 *********/
 /// <summary>Get whether a given item is a crop compatible with the seed marker.</summary>
 /// <param name="item">The item to check.</param>
 private bool IsValidCrop(ITrackedStack item)
 {
     return
         (item.Type == ItemType.Object &&
          item.Sample.ParentSheetIndex != 433 && // seed maker doesn't allow coffee beans
          SeedMakerMachine.SeedLookup.ContainsKey(item.Sample.ParentSheetIndex));
 }
Esempio n. 6
0
        public ITrackedStack GetOutput()
        {
            this.PfmMachine.GetOutput();
            ITrackedStack trackedStack = this.ClonerMachine.GetOutput();

            this.PfmMachine.Reset(_machine.heldObject.Value);
            return(trackedStack);
        }
Esempio n. 7
0
 /// <summary>
 /// Checks if this recipe accepts the given input.
 /// </summary>
 /// <returns><c>true</c>, if input is accepted, <c>false</c> otherwise.</returns>
 /// <param name="stack">Stack.</param>
 public bool AcceptsInput(ITrackedStack stack)
 {
     if (stack.Sample is SObject @object)
     {
         return(_underlying.AcceptsInput(@object));
     }
     return(false);
 }
Esempio n. 8
0
 public void Store(ITrackedStack stack)
 {
     if (targetMachineGroup == null)
     {
         return;
     }
     targetMachineGroup?.StorageManager.TryPush(stack);
 }
        /// <summary>Get whether the underlying items can stack with the items in another stack, based on their respective <see cref="Sample"/> values.</summary>
        /// <param name="stack">The other stack to check.</param>
        public bool CanStackWith(ITrackedStack stack)
        {
            if (stack?.Sample == null)
            {
                return(false);
            }

            return(this.Sample == null || this.Sample.canStackWith(stack.Sample));
        }
Esempio n. 10
0
        /****
        ** TryPush
        ****/
        /// <summary>Add the given item stack to the pipes if there's space.</summary>
        /// <param name="item">The item stack to push.</param>
        public bool TryPush(ITrackedStack item)
        {
            if (item == null || item.Count <= 0)
            {
                return(false);
            }

            int originalCount = item.Count;

            // push into 'output' chests
            foreach (IContainer container in this.Containers)
            {
                if (container.Name.IndexOf("output", StringComparison.InvariantCultureIgnoreCase) < 0)
                {
                    continue;
                }

                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into chests that already have this item
            string itemKey = this.GetItemKey(item.Sample);

            foreach (IContainer container in this.Containers)
            {
                if (container.All(p => this.GetItemKey(p.Sample) != itemKey))
                {
                    continue;
                }

                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into first available chest
            if (item.Count >= 0)
            {
                foreach (IContainer container in this.Containers)
                {
                    container.Store(item);
                    if (item.Count <= 0)
                    {
                        return(true);
                    }
                }
            }

            return(item.Count < originalCount);
        }
Esempio n. 11
0
        /****
        ** TryPush
        ****/
        /// <summary>Add the given item stack to the pipes if there's space.</summary>
        /// <param name="item">The item stack to push.</param>
        public bool TryPush(ITrackedStack item)
        {
            if (item == null || item.Count <= 0)
            {
                return(false);
            }

            int originalCount = item.Count;

            var preferOutputContainers = this.Containers.Where(p => p.AllowsInput() && p.PreferForOutput());
            var otherContainers        = this.Containers.Where(p => p.AllowsInput() && !p.PreferForOutput());

            // push into 'output' chests
            foreach (IContainer container in preferOutputContainers)
            {
                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into chests that already have this item
            string itemKey = this.GetItemKey(item.Sample);

            foreach (IContainer container in otherContainers)
            {
                if (container.All(p => this.GetItemKey(p.Sample) != itemKey))
                {
                    continue;
                }

                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into first available chest
            if (item.Count >= 0)
            {
                foreach (IContainer container in otherContainers)
                {
                    container.Store(item);
                    if (item.Count <= 0)
                    {
                        return(true);
                    }
                }
            }

            return(item.Count < originalCount);
        }
        /****
        ** TryPush
        ****/
        /// <summary>Add the given item stack to the pipes if there's space.</summary>
        /// <param name="item">The item stack to push.</param>
        public bool TryPush(ITrackedStack item)
        {
            if (item == null || item.Count <= 0)
            {
                return(false);
            }

            int originalCount = item.Count;

            IContainer[] preferredContainers = this.InputContainers.TakeWhile(p => p.StoragePreferred()).ToArray();
            IContainer[] otherContainers     = this.InputContainers.Skip(preferredContainers.Length).ToArray();

            // push into 'output' chests
            foreach (IContainer container in preferredContainers)
            {
                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into chests that already have this item
            string itemKey = this.GetItemKey(item.Sample);

            foreach (IContainer container in otherContainers)
            {
                if (container.All(p => this.GetItemKey(p.Sample) != itemKey))
                {
                    continue;
                }

                container.Store(item);
                if (item.Count <= 0)
                {
                    return(true);
                }
            }

            // push into first available chest
            if (item.Count >= 0)
            {
                foreach (IContainer container in otherContainers)
                {
                    container.Store(item);
                    if (item.Count <= 0)
                    {
                        return(true);
                    }
                }
            }

            return(item.Count < originalCount);
        }
Esempio n. 13
0
 /// <summary>Returns an enumerator that iterates through the collection.</summary>
 /// <returns>An enumerator that can be used to iterate through the collection.</returns>
 public IEnumerator <ITrackedStack> GetEnumerator()
 {
     foreach (Item item in this.GetInventory().ToArray())
     {
         ITrackedStack stack = this.GetTrackedItem(item);
         if (stack != null)
         {
             yield return(stack);
         }
     }
 }
        /// <summary>Add a stack to the collection.</summary>
        /// <param name="stack">The item stack to add.</param>
        public void Add(ITrackedStack stack)
        {
            if (stack?.Sample == null)
            {
                throw new InvalidOperationException("Can't track an item with no underlying item.");
            }

            this.Stacks.Add(stack);
            if (this.Sample == null)
            {
                this.Sample = stack.Sample;
            }
        }
Esempio n. 15
0
        /// <summary>Add a stack to the collection.</summary>
        /// <param name="stack">The item stack to add.</param>
        public void Add(ITrackedStack stack)
        {
            if (stack == null)
            {
                throw new ArgumentNullException(nameof(stack));
            }
            if (stack.Sample == null)
            {
                throw new InvalidOperationException("Can't track an item with no underlying item.");
            }

            this.Stacks.Add(stack);
        }
Esempio n. 16
0
        /// <summary>Pull items from the connected pipes.</summary>
        /// <param name="pipes">The connected IO pipes.</param>
        /// <returns>Returns whether the machine started processing an item.</returns>
        public bool Pull(IPipe[] pipes)
        {
            ITrackedStack tracker = pipes.GetItems(p => p.Sample is SObject obj && obj.canBeShipped()).Take(1).FirstOrDefault();

            if (tracker != null)
            {
                SObject item = (SObject)tracker.Take(tracker.Count);
                this.Farm.shippingBin.Add(item);
                this.Farm.lastItemShipped = item;
                this.Farm.showShipment(item, false);
                return(true);
            }
            return(false);
        }
        /// <summary>Provide input to the machine.</summary>
        /// <param name="input">The available items.</param>
        /// <returns>Returns whether the machine started processing an item.</returns>
        public override bool SetInput(IStorage input)
        {
            ITrackedStack tracker = input.GetItems().Where(p => p.Sample is SObject obj && obj.canBeShipped()).Take(1).FirstOrDefault();

            if (tracker != null)
            {
                SObject item = (SObject)tracker.Take(tracker.Count);
                this.Farm.getShippingBin(Game1.MasterPlayer).Add(item);
                this.Farm.lastItemShipped = item;
                this.Farm.showShipment(item, false);
                return(true);
            }
            return(false);
        }
Esempio n. 18
0
        /// <summary>Add an item to its corresponding stack or add a new stack.</summary>
        /// <param name="input">The input item to add.</param>
        /// <returns>Returns the affected stack.</returns>
        public TrackedItemCollection Add(ITrackedStack input)
        {
            TrackedItemCollection?stack = this.FirstOrDefault(p => p.CanStackWith(input));

            if (stack != null)
            {
                stack.Add(input);
            }
            else
            {
                base.Add(stack = new TrackedItemCollection(input));
            }

            return(stack);
        }
Esempio n. 19
0
        /*********
        ** Private methods
        *********/
        /// <summary>Try to add an item to the input queue, and adjust its stack size accordingly.</summary>
        /// <param name="item">The item stack to add.</param>
        /// <returns>Returns whether any items were taken from the stack.</returns>
        private bool TryAddInput(ITrackedStack item)
        {
            // nothing to add
            if (item.Count <= 0)
            {
                return(false);
            }

            // clean up input bin
            this.Input.clearNulls();

            // try adding to input
            int          originalSize = item.Count;
            IList <Item> slots        = this.Input.items;
            int          maxStackSize = this.GetMaxInputStackSize(item.Sample);

            for (int i = 0; i < Chest.capacity; i++)
            {
                // done
                if (item.Count <= 0)
                {
                    break;
                }

                // add to existing slot
                if (slots.Count > i)
                {
                    Item slot = slots[i];
                    if (item.Sample.canStackWith(slot) && slot.Stack < maxStackSize)
                    {
                        var sample = item.Sample.getOne();
                        sample.Stack = Math.Min(item.Count, maxStackSize - slot.Stack); // the most items we can add to the stack (in theory)
                        int actualAdded = sample.Stack - slot.addToStack(sample);       // how many items were actually added to the stack
                        item.Reduce(actualAdded);
                    }
                    continue;
                }

                // add to new slot
                slots.Add(item.Take(Math.Min(item.Count, maxStackSize)));
            }

            return(item.Count < originalSize);
        }
Esempio n. 20
0
        /// <summary>Store an item stack.</summary>
        /// <param name="stack">The item stack to store.</param>
        /// <remarks>If the storage can't hold the entire stack, it should reduce the tracked stack accordingly.</remarks>
        public void Store(ITrackedStack stack)
        {
            if (stack.Count <= 0 || this.Chest.SpecialChestType == Chest.SpecialChestTypes.AutoLoader)
            {
                return;
            }

            IList <Item> inventory = this.GetInventory();

            // try stack into existing slot
            foreach (Item slot in inventory)
            {
                if (slot != null && stack.Sample.canStackWith(slot))
                {
                    Item sample = stack.Sample.getOne();
                    sample.Stack = stack.Count;
                    int added = stack.Count - slot.addToStack(sample);
                    stack.Reduce(added);
                    if (stack.Count <= 0)
                    {
                        return;
                    }
                }
            }

            // try add to empty slot
            int capacity = this.Chest.GetActualCapacity();

            for (int i = 0; i < capacity && i < inventory.Count; i++)
            {
                if (inventory[i] == null)
                {
                    inventory[i] = stack.Take(stack.Count);
                    return;
                }
            }

            // try add new slot
            if (inventory.Count < capacity)
            {
                inventory.Add(stack.Take(stack.Count));
            }
        }
        /// <summary>Add the given item stack to the pipes if there's space.</summary>
        /// <param name="pipes">The pipes to fill.</param>
        /// <param name="item">The item stack to push.</param>
        public static bool TryPush(this IPipe[] pipes, ITrackedStack item)
        {
            if (item == null || item.Count <= 0)
            {
                return(false);
            }

            int originalCount = item.Count;

            foreach (IPipe pipe in pipes)
            {
                pipe.Store(item);
                if (item.Count <= 0)
                {
                    break;
                }
            }

            return(item.Count < originalCount);
        }
Esempio n. 22
0
        /****
        ** TryPush
        ****/
        /// <summary>Add the given item stack to the pipes if there's space.</summary>
        /// <param name="item">The item stack to push.</param>
        public bool TryPush(ITrackedStack item)
        {
            if (item == null || item.Count <= 0)
            {
                return(false);
            }

            int originalCount = item.Count;

            foreach (IContainer container in this.Containers)
            {
                container.Store(item);
                if (item.Count <= 0)
                {
                    break;
                }
            }

            return(item.Count < originalCount);
        }
Esempio n. 23
0
        /// <summary>Store an item stack.</summary>
        /// <param name="stack">The item stack to store.</param>
        /// <remarks>If the storage can't hold the entire stack, it should reduce the tracked stack accordingly.</remarks>
        public void Store(ITrackedStack stack)
        {
            if (stack.Count <= 0)
            {
                return;
            }

            IList <Item> inventory = this.Chest.items;

            // try stack into existing slot
            foreach (Item slot in inventory)
            {
                if (slot != null && stack.Sample.canStackWith(slot))
                {
                    int added = stack.Count - slot.addToStack(stack.Count);
                    stack.Reduce(added);
                    if (stack.Count <= 0)
                    {
                        return;
                    }
                }
            }

            // try add to empty slot
            for (int i = 0; i < Chest.capacity && i < inventory.Count; i++)
            {
                if (inventory[i] == null)
                {
                    inventory[i] = stack.Take(stack.Count);
                    return;
                }
            }

            // try add new slot
            if (inventory.Count < Chest.capacity)
            {
                inventory.Add(stack.Take(stack.Count));
            }
        }
Esempio n. 24
0
        /// <summary>Find items in the pipe matching a predicate.</summary>
        /// <param name="predicate">Matches items that should be returned.</param>
        /// <param name="count">The number of items to find.</param>
        /// <remarks>If there aren't enough items in the pipe, it should return those it has.</remarks>
        private IEnumerable <ITrackedStack> GetImpl(Func <Item, bool> predicate, int count)
        {
            int countFound = 0;

            foreach (Item item in this.GetInventory())
            {
                if (item != null && predicate(item))
                {
                    ITrackedStack stack = this.GetTrackedItem(item);
                    if (stack == null)
                    {
                        continue;
                    }

                    countFound += item.Stack;
                    yield return(stack);

                    if (countFound >= count)
                    {
                        yield break;
                    }
                }
            }
        }
Esempio n. 25
0
 /// <summary>Get whether the recipe can accept a given item as input (regardless of stack size).</summary>
 /// <param name="stack">The item to check.</param>
 public bool AcceptsInput(ITrackedStack stack)
 {
     return
         ((this.Type == null || stack.Type == this.Type) &&
          (stack.Sample.ParentSheetIndex == this.InputID || stack.Sample.Category == this.InputID));
 }
        /// <inheritdoc/>
        public bool Automate_SetInput(IStorage input, MassProductionMachineDefinition mpm, IMachine originalMachine, SObject originalMachineObject)
        {
            if (mpm != null)
            {
                try
                {
                    ITrackedStack crop          = null;
                    InputInfo     cropInfo      = null;
                    int           inputQuantity = 0;

                    foreach (ITrackedStack item in input.GetItems())
                    {
                        if (IsValidCrop(item))
                        {
                            InputInfo inputInfo = new InputInfo()
                            {
                                ID           = item.Sample.ParentSheetIndex,
                                Name         = item.Sample.Name,
                                Quality      = 0,
                                IsFuel       = false,
                                BaseQuantity = 1
                            };

                            if (item.Sample is SObject obj)
                            {
                                inputInfo.Quality = obj.Quality;
                            }

                            inputQuantity = mpm.Settings.CalculateInputRequired(inputInfo);

                            if (item.Count >= inputQuantity)
                            {
                                crop     = item;
                                cropInfo = inputInfo;
                                break;
                            }
                        }
                    }

                    if (crop != null)
                    {
                        crop.Reduce(inputQuantity);
                        int seedID = SEED_LOOKUP[crop.Sample.ParentSheetIndex];

                        Random random = new Random((int)Game1.stats.DaysPlayed +
                                                   (int)Game1.uniqueIDForThisGame / 2 + (int)originalMachineObject.TileLocation.X + (int)originalMachineObject.TileLocation.Y * 77 + Game1.timeOfDay);
                        int outputID       = seedID;
                        int outputQuantity = mpm.Settings.CalculateOutputProduced(random.Next(1, 4), cropInfo);

                        if (random.NextDouble() < 0.005)
                        {
                            outputID       = 499;
                            outputQuantity = mpm.Settings.CalculateOutputProduced(1, cropInfo);
                        }
                        else if (random.NextDouble() < 0.02)
                        {
                            outputID       = 770;
                            outputQuantity = mpm.Settings.CalculateOutputProduced(random.Next(1, 5), cropInfo);
                        }

                        originalMachineObject.heldObject.Value  = new SObject(outputID, outputQuantity);
                        originalMachineObject.MinutesUntilReady = mpm.Settings.CalculateTimeRequired(20);

                        return(true);
                    }
                }
                catch (Exception e)
                {
                    ModEntry.Instance.Monitor.Log($"{e}", StardewModdingAPI.LogLevel.Error);
                }
            }
            else
            {
                return(originalMachine.SetInput(input));
            }

            return(false);
        }
Esempio n. 27
0
 /*********
 ** Public methods
 *********/
 /// <summary>Construct an instance.</summary>
 /// <param name="consumables">The matching items available to consume.</param>
 /// <param name="countNeeded">The number of items needed for the recipe.</param>
 public Consumable(ITrackedStack consumables, int countNeeded)
 {
     this.Consumables = consumables;
     this.CountNeeded = countNeeded;
     this.IsMet       = consumables.Count >= countNeeded;
 }
Esempio n. 28
0
 /*********
 ** Public methods
 *********/
 /// <summary>Get whether a given item is a crop compatible with the seed marker.</summary>
 /// <param name="item">The item to check.</param>
 public bool IsValidCrop(ITrackedStack item)
 {
     return
         (item.Sample.ParentSheetIndex != 433 && // seed maker doesn't allow coffee beans
          SeedMakerMachine.CropSeedIDs.ContainsKey(item.Sample.ParentSheetIndex));
 }
Esempio n. 29
0
 /// <summary>Get whether the recipe can accept a given item as input (regardless of stack size).</summary>
 /// <param name="stack">The item to check.</param>
 public bool AcceptsInput(ITrackedStack stack)
 {
     return(stack.Sample.parentSheetIndex == this.InputID || stack.Sample.category == this.InputID);
 }
Esempio n. 30
0
        /// <inheritdoc />
        public void Automate()
        {
            IStorage storage = this.StorageManager;
            double   curTime = Game1.currentGameTime.TotalGameTime.TotalMilliseconds;

            // clear expired timers
            if (this.MachinePauseExpiries.Count > 0)
            {
                foreach (var entry in this.MachinePauseExpiries.ToArray())
                {
                    if (curTime >= entry.Value)
                    {
                        this.MachinePauseExpiries.Remove(entry.Key);
                    }
                }
            }

            // get machines ready for input/output
            IList <IMachine> outputReady = new List <IMachine>();
            IList <IMachine> inputReady  = new List <IMachine>();

            foreach (IMachine machine in this.Machines)
            {
                if (this.MachinePauseExpiries.ContainsKey(machine))
                {
                    continue;
                }

                switch (machine.GetState())
                {
                case MachineState.Done:
                    outputReady.Add(machine);
                    break;

                case MachineState.Empty:
                    inputReady.Add(machine);
                    break;
                }
            }
            if (!outputReady.Any() && !inputReady.Any())
            {
                return;
            }

            // process output
            foreach (IMachine machine in outputReady)
            {
                ITrackedStack output = null;
                try
                {
                    output = machine.GetOutput();
                    if (storage.TryPush(output) && machine.GetState() == MachineState.Empty)
                    {
                        inputReady.Add(machine);
                    }
                }
                catch (Exception ex)
                {
                    string error = $"Failed to automate machine '{machine.MachineTypeID}' at {machine.Location?.Name} (tile: {machine.TileArea.X}, {machine.TileArea.Y}). An error occurred while ";
                    if (output == null)
                    {
                        error += "retrieving its output.";
                    }
                    else
                    {
                        error += $"storing its output item #{output.Sample.ParentSheetIndex} ('{output.Sample.Name}'";
                        if (output.Sample is SObject outputObj && outputObj.preservedParentSheetIndex.Value >= 0)
                        {
                            error += $", preserved item #{outputObj.preservedParentSheetIndex.Value}";
                        }
                        error += ").";
                    }
                    error += $" Machine paused for {this.ErrorPauseMilliseconds / 1000}s.";

                    this.MachinePauseExpiries[machine] = curTime + this.ErrorPauseMilliseconds;
                    throw new InvalidOperationException(error, ex);
                }
            }

            // process input
            HashSet <string> ignoreMachines = new HashSet <string>();

            foreach (IMachine machine in inputReady)
            {
                if (ignoreMachines.Contains(machine.MachineTypeID))
                {
                    continue;
                }

                if (!machine.SetInput(storage))
                {
                    ignoreMachines.Add(machine.MachineTypeID); // if the machine can't process available input, no need to ask every instance of its type
                }
            }
        }