/// <summary>
 /// Check if an input is excluded by a producer rule.
 /// </summary>
 /// <param name="producerRule">The producer rule to check.</param>
 /// <param name="input">The input to check</param>
 /// <returns>true if should be excluded</returns>
 public static bool IsInputExcluded(ProducerRule producerRule, Object input)
 {
     return(producerRule.ExcludeIdentifiers != null && (producerRule.ExcludeIdentifiers.Contains(input.ParentSheetIndex.ToString()) ||
                                                        producerRule.ExcludeIdentifiers.Contains(input.Name) ||
                                                        producerRule.ExcludeIdentifiers.Contains(input.Category.ToString()) ||
                                                        producerRule.ExcludeIdentifiers.Intersect(input.GetContextTags()).Any()));
 }
 /// <summary>
 /// Adds or replace a custom producer rule to the game.
 /// You should probably call this method everytime a save game loads, to ensure all custom objects are properly loaded.
 /// Producer rule should be unique per name of producer and input identifier.
 /// </summary>
 /// <param name="producerRule">The producer rule to be added or replaced.</param>
 /// <param name="i18n">Optional i18n object, to look for the output name in case a translation key was used.</param>
 /// <param name="modUniqueId">The mod unique id.</param>
 public static void AddProducerItems(ProducerRule producerRule, ITranslationHelper i18n = null, string modUniqueId = null)
 {
     AddProducerItems(new List <ProducerRule>()
     {
         producerRule
     }, i18n, modUniqueId);
 }
Exemple #3
0
 /// <summary>
 /// Check if a farmer has the required fules and stack for a given producer rule.
 /// </summary>
 /// <param name="producerRule">the producer tule to check</param>
 /// <param name="who">The farmer to check</param>
 public static void ValidateIfAnyFuelStackLessThanRequired(ProducerRule producerRule, Farmer who, bool probe)
 {
     foreach (Tuple <int, int> fuel in producerRule.FuelList)
     {
         if (!who.hasItemInInventory(fuel.Item1, fuel.Item2))
         {
             if (!probe)
             {
                 if (fuel.Item1 >= 0)
                 {
                     Dictionary <int, string> objects = DataLoader.Helper.Content.Load <Dictionary <int, string> >("Data\\ObjectInformation", ContentSource.GameContent);
                     var objectName = Lexicon.makePlural(ObjectUtils.GetObjectParameter(objects[fuel.Item1], (int)ObjectParameter.DisplayName), fuel.Item2 == 1);
                     throw new RestrictionException(DataLoader.Helper.Translation.Get("Message.Requirement.Amount", new { amount = fuel.Item2, objectName }));
                 }
                 else
                 {
                     var objectName = ObjectUtils.GetCategoryName(fuel.Item1);
                     throw new RestrictionException(DataLoader.Helper.Translation.Get("Message.Requirement.Amount", new { amount = fuel.Item2, objectName }));
                 }
             }
             else
             {
                 throw new RestrictionException();
             }
         }
     }
 }
 /// <summary>
 /// Check if a farmer has the required fules and stack for a given producer rule.
 /// </summary>
 /// <param name="producerRule">the producer tule to check</param>
 /// <param name="who">The farmer to check</param>
 /// <param name="shouldDisplayMessages">If an ingame message should be shown when the fuel is not enough</param>
 /// <returns></returns>
 public static bool IsAnyFuelStackLessThanRequired(ProducerRule producerRule, Farmer who, bool shouldDisplayMessages)
 {
     foreach (Tuple <int, int> fuel in producerRule.FuelList)
     {
         if (!who.hasItemInInventory(fuel.Item1, fuel.Item2))
         {
             if (shouldDisplayMessages)
             {
                 if (fuel.Item1 >= 0)
                 {
                     Dictionary <int, string> objects = DataLoader.Helper.Content.Load <Dictionary <int, string> >("Data\\ObjectInformation", ContentSource.GameContent);
                     var objectName = Lexicon.makePlural(ObjectUtils.GetObjectParameter(objects[fuel.Item1], (int)ObjectParameter.DisplayName), fuel.Item2 == 1);
                     Game1.showRedMessage(DataLoader.Helper.Translation.Get("Message.Requirement.Amount", new { amount = fuel.Item2, objectName }));
                 }
                 else
                 {
                     var objectName = ObjectUtils.GetCategoryName(fuel.Item1);
                     Game1.showRedMessage(DataLoader.Helper.Translation.Get("Message.Requirement.Amount", new { amount = fuel.Item2, objectName }));
                 }
             }
             return(true);
         }
     }
     return(false);
 }
Exemple #5
0
 /// <summary>
 /// Adds or replace a custom producer rule to the game.
 /// You should probably call this method everytime a save game loads, to ensure all custom objects are properly loaded.
 /// Producer rule should be unique per name of producer and input identifier.
 /// </summary>
 /// <param name="producerRule">The producer rule to be added or replaced.</param>
 /// <param name="i18n">Optional i18n object, to look for the output name in case a translation key was used.</param>
 public static void AddProducerItems(ProducerRule producerRule, ITranslationHelper i18n = null)
 {
     AddProducerItems(new List <ProducerRule>()
     {
         producerRule
     }, i18n);
 }
        private static void AddRuleToRepository(ProducerRule producerRule)
        {
            Tuple <string, object> ruleKey = new Tuple <string, object>(producerRule.ProducerName, producerRule.InputKey);

            if (RulesRepository.ContainsKey(ruleKey))
            {
                ProducerRule oldRule = RulesRepository[ruleKey];
                if (oldRule.ModUniqueID != producerRule.ModUniqueID)
                {
                    if (oldRule.OverrideMod.Contains(producerRule.ModUniqueID) &&
                        producerRule.OverrideMod.Contains(oldRule.ModUniqueID))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log(
                            $"Both mod '{oldRule.ModUniqueID}' and '{producerRule.ModUniqueID}' are saying they should override the rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. You should report the problem to these mod's authors. Rule from mod '{oldRule.ModUniqueID}' will be used.",
                            LogLevel.Warn);
                        return;
                    }
                    else if (producerRule.OverrideMod.Contains(oldRule.ModUniqueID))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log(
                            $"Mod '{producerRule.ModUniqueID}' is overriding mod '{oldRule.ModUniqueID}' rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'.",
                            LogLevel.Debug);
                    }
                    else
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log(
                            $"Mod '{producerRule.ModUniqueID}' can't override mod '{oldRule.ModUniqueID}' rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.",
                            LogLevel.Debug);
                        return;
                    }
                }
            }
            RulesRepository[ruleKey] = producerRule;
        }
Exemple #7
0
        public static OutputConfig ProduceOutput(ProducerRule producerRule, Object producer,
                                                 Func <int, int, bool> fuelSearch, Farmer who, GameLocation location
                                                 , ProducerConfig producerConfig = null, Object input = null
                                                 , bool probe = false, bool noSoundAndAnimation       = false)
        {
            if (who == null)
            {
                who = Game1.getFarmer((long)producer.owner);
            }
            Vector2      tileLocation = producer.TileLocation;
            Random       random       = ProducerRuleController.GetRandomForProducing(tileLocation);
            OutputConfig outputConfig = OutputConfigController.ChooseOutput(producerRule.OutputConfigs, random, fuelSearch, location, input);

            if (outputConfig != null)
            {
                Object output = producerRule.LookForInputWhenReady == null?OutputConfigController.CreateOutput(outputConfig, input, random) : new Object(outputConfig.OutputIndex, 1);

                producer.heldObject.Value = output;
                if (!probe)
                {
                    if (producerRule.LookForInputWhenReady == null)
                    {
                        OutputConfigController.LoadOutputName(outputConfig, producer.heldObject.Value, input, who);
                    }

                    if (!noSoundAndAnimation)
                    {
                        SoundUtil.PlaySound(producerRule.Sounds, location);
                        SoundUtil.PlayDelayedSound(producerRule.DelayedSounds, location);
                    }

                    producer.minutesUntilReady.Value = outputConfig.MinutesUntilReady ?? producerRule.MinutesUntilReady;
                    if (producerRule.SubtractTimeOfDay)
                    {
                        producer.minutesUntilReady.Value = Math.Max(producer.minutesUntilReady.Value - Game1.timeOfDay, 1);
                    }

                    if (producerConfig != null)
                    {
                        producer.showNextIndex.Value = producerConfig.AlternateFrameProducing;
                    }

                    if (producerRule.PlacingAnimation.HasValue && !noSoundAndAnimation)
                    {
                        AnimationController.DisplayAnimation(producerRule.PlacingAnimation.Value,
                                                             producerRule.PlacingAnimationColor, location, tileLocation,
                                                             new Vector2(producerRule.PlacingAnimationOffsetX, producerRule.PlacingAnimationOffsetY));
                    }

                    if (location.hasLightSource(LightSourceConfigController.GenerateIdentifier(tileLocation)))
                    {
                        location.removeLightSource(LightSourceConfigController.GenerateIdentifier(tileLocation));
                    }
                    producer.initializeLightSource(tileLocation, false);

                    producerRule.IncrementStatsOnInput.ForEach(s => StatsController.IncrementStardewStats(s, producerRule.InputStack));
                }
            }
            return(outputConfig);
        }
Exemple #8
0
        /// <summary>
        /// Check if an input is excluded by a producer rule or a mass production machine definition.
        /// Adapted from https://github.com/Digus/StardewValleyMods/blob/master/ProducerFrameworkMod/ProducerRuleController.cs
        /// </summary>
        /// <param name="producerRule">The producer rule to check.</param>
        /// <param name="mpm">The definition of the mass production machine to check.</param>
        /// <param name="input">The input to check.</param>
        /// <returns>True if it should be excluded.</returns>
        public static bool IsInputExcluded(ProducerRule producerRule, MassProductionMachineDefinition mpm, SObject input)
        {
            bool isExcludedByRule = producerRule.ExcludeIdentifiers != null &&
                                    (producerRule.ExcludeIdentifiers.Contains(input.ParentSheetIndex.ToString()) || producerRule.ExcludeIdentifiers.Contains(input.Name) ||
                                     producerRule.ExcludeIdentifiers.Contains(input.Category.ToString()) || producerRule.ExcludeIdentifiers.Intersect(input.GetContextTags()).Any());
            bool isExcludedByMPM = mpm.BlacklistedInputKeys.Contains(input.ParentSheetIndex.ToString()) || mpm.BlacklistedInputKeys.Contains(input.Name) ||
                                   mpm.BlacklistedInputKeys.Contains(input.Category.ToString()) || mpm.BlacklistedInputKeys.Intersect(input.GetContextTags()).Any();

            return(isExcludedByRule || isExcludedByMPM);
        }
        /// <summary>
        /// Check if an input has the required stack for the producer rule.
        /// </summary>
        /// <param name="producerRule">the producer rule to check</param>
        /// <param name="input">The input to check</param>
        public static void ValidateIfInputStackLessThanRequired(ProducerRule producerRule, Object input)
        {
            int requiredStack = producerRule.InputStack;

            if (input.Stack < requiredStack)
            {
                throw new RestrictionException(DataLoader.Helper.Translation.Get(
                                                   "Message.Requirement.Amount"
                                                   , new { amount = requiredStack, objectName = Lexicon.makePlural(input.DisplayName, requiredStack == 1) }
                                                   ));
            }
        }
Exemple #10
0
        /// <summary>
        /// Converts a PFM producer rule's input and fuels into a list of input info.
        /// </summary>
        /// <param name="producerRule"></param>
        /// <returns></returns>
        public static List <InputInfo> ConvertPFMInputs(ProducerRule producerRule, SObject inputObject)
        {
            List <InputInfo> inputs = new List <InputInfo>();

            if (inputObject != null)
            {
                inputs.Add(ConvertInput(inputObject, producerRule.InputStack));
                inputs.AddRange(GetFromFuelList(producerRule.FuelList));
            }

            return(inputs);
        }
Exemple #11
0
        /// <summary>
        /// Check if the input and fuels are of the required quantities for the producer rule and machine settings.
        /// Adapted from https://github.com/Digus/StardewValleyMods/blob/master/ProducerFrameworkMod/ProducerRuleController.cs
        /// </summary>
        /// <param name="producerRule">the producer rule to check.</param>
        /// <param name="settings">Mass production machine settings to check.</param>
        /// <param name="inputs">The inputs to check.</param>
        /// <param name="who">The farmer placing an item in the machine.</param>
        public static void ValidateIfInputsLessThanRequired(ProducerRule producerRule, MPMSettings settings, List <InputInfo> inputs, Farmer who)
        {
            foreach (InputInfo input in inputs)
            {
                int quantityRequired = settings.CalculateInputRequired(input);

                if ((!input.IsFuel && who.ActiveObject.Stack < quantityRequired) ||
                    (input.IsFuel && !who.hasItemInInventory(input.ID, quantityRequired)))
                {
                    throw new RestrictionException(string.Format("{1}x{0} required", quantityRequired, input.Name));
                }
            }
        }
 private List<IConsumable> GetRequiredFuels(ProducerRule producerRule, IStorage storage)
 {
     List<IConsumable> requiredFuels =  new List<IConsumable>();
     foreach (Tuple<int, int> requiredFuel in producerRule.FuelList)
     {
         if (!storage.TryGetIngredient(requiredFuel.Item1, requiredFuel.Item2, out IConsumable fuel))
         {
             return null;
         }
         requiredFuels.Add(fuel);
     }
     return requiredFuels;
 }
        /// <summary>
        /// Check if an input has the required stack for the producer rule.
        /// </summary>
        /// <param name="producerRule">the producer tule to check</param>
        /// <param name="input">The input to check</param>
        /// <param name="shouldDisplayMessages">If an ingame message should be shown when the input is not enough</param>
        /// <returns>true if the stack if not enough</returns>
        public static bool IsInputStackLessThanRequired(ProducerRule producerRule, Object input, bool shouldDisplayMessages)
        {
            int requiredStack = producerRule.InputStack;

            if (input.Stack < requiredStack)
            {
                if (shouldDisplayMessages)
                {
                    Game1.showRedMessage(DataLoader.Helper.Translation.Get(
                                             "Message.Requirement.Amount"
                                             , new { amount = requiredStack, objectName = Lexicon.makePlural(input.DisplayName, requiredStack == 1) }
                                             ));
                }
                return(true);
            }
            return(false);
        }
Exemple #14
0
        /// <summary>
        /// Makes the machine produce an appropriate output.
        /// Adapted from https://github.com/Digus/StardewValleyMods/blob/master/ProducerFrameworkMod/OutputConfigController.cs
        /// </summary>
        /// <param name="producerRule"></param>
        /// <param name="settings"></param>
        /// <param name="producer"></param>
        /// <param name="fuelSearch"></param>
        /// <param name="who"></param>
        /// <param name="location"></param>
        /// <param name="producerConfig"></param>
        /// <param name="input"></param>
        /// <param name="probe"></param>
        /// <param name="noSoundAndAnimation"></param>
        /// <returns>Base output config - no values altered for mass production machine</returns>
        public static OutputConfig ProduceOutput(ProducerRule producerRule, MPMSettings settings, SObject producer, Func <int, int, bool> fuelSearch, Farmer who, GameLocation location,
                                                 ProducerConfig producerConfig = null, SObject input = null, int inputQuantity = 0, bool probe = false, bool noSoundAndAnimation = false,
                                                 List <InputInfo> inputInfo    = null)
        {
            if (who == null)
            {
                who = Game1.getFarmer((long)producer.owner);
            }

            if (inputInfo == null)
            {
                inputInfo = new List <InputInfo>();
            }

            Vector2      tileLocation = producer.TileLocation;
            Random       random       = ProducerRuleController.GetRandomForProducing(tileLocation);
            OutputConfig outputConfig = OutputConfigController.ChooseOutput(producerRule.OutputConfigs, random, fuelSearch, location, input);

            if (outputConfig != null)
            {
                SObject output = producerRule.LookForInputWhenReady == null?OutputConfigController.CreateOutput(outputConfig, input, random) : new SObject(outputConfig.OutputIndex, 1);

                output.Stack = settings.CalculateOutputProduced(output.Stack, inputInfo.ToArray());

                if (settings.Quality.HasValue)
                {
                    if (settings.Quality == QualitySetting.KeepInput)
                    {
                        output.Quality = input.Quality;
                    }
                    else
                    {
                        output.Quality = settings.GetOutputQuality();
                    }
                }

                producer.heldObject.Value = output;

                if (!probe)
                {
                    if (producerRule.LookForInputWhenReady == null)
                    {
                        OutputConfigController.LoadOutputName(outputConfig, producer.heldObject.Value, input, who);
                    }

                    //if (!noSoundAndAnimation)
                    //{
                    //    SoundUtil.PlaySound(producerRule.Sounds, location);
                    //    SoundUtil.PlayDelayedSound(producerRule.DelayedSounds, location);
                    //}

                    int minutesUntilReadyBase = outputConfig.MinutesUntilReady ?? producerRule.MinutesUntilReady;
                    int minutesUntilReady     = settings.CalculateTimeRequired(minutesUntilReadyBase, inputInfo.ToArray());

                    producer.minutesUntilReady.Value = minutesUntilReady;
                    if (producerRule.SubtractTimeOfDay)
                    {
                        producer.minutesUntilReady.Value = Math.Max(producer.minutesUntilReady.Value - Game1.timeOfDay, 1);
                    }

                    if (producerConfig != null)
                    {
                        producer.showNextIndex.Value = producerConfig.AlternateFrameProducing;
                    }

                    //if (producerRule.PlacingAnimation.HasValue && !noSoundAndAnimation)
                    //{
                    //    AnimationController.DisplayAnimation(producerRule.PlacingAnimation.Value,
                    //        producerRule.PlacingAnimationColor, location, tileLocation,
                    //        new Vector2(producerRule.PlacingAnimationOffsetX, producerRule.PlacingAnimationOffsetY));
                    //}

                    if (location.hasLightSource(LightSourceConfigController.GenerateIdentifier(tileLocation)))
                    {
                        location.removeLightSource(LightSourceConfigController.GenerateIdentifier(tileLocation));
                    }
                    producer.initializeLightSource(tileLocation, false);

                    int statsIncrement = inputQuantity;
                    producerRule.IncrementStatsOnInput.ForEach(s => StatsController.IncrementStardewStats(s, statsIncrement));
                }
            }
            return(outputConfig);
        }
        /// <summary>
        /// Adds or replace a list of custom producer rules to the game.
        /// You should probably call this method everytime a save game loads, to ensure all custom objects are properly loaded.
        /// Producer rules should be unique per name of producer and input identifier.
        /// </summary>
        /// <param name="producerRules">A list of producer rules to be added or replaced.</param>
        /// <param name="i18n">Optional i18n object, to look for the output name in case a translation key was used.</param>
        /// <param name="modUniqueId">The mod unique id.</param>
        public static void AddProducerItems(List <ProducerRule> producerRules, ITranslationHelper i18n = null, string modUniqueId = null)
        {
            Dictionary <int, string> objects = DataLoader.Helper.Content.Load <Dictionary <int, string> >("Data\\ObjectInformation", ContentSource.GameContent);

            foreach (var producerRule in producerRules)
            {
                try {
                    producerRule.ModUniqueID = modUniqueId;
                    if (String.IsNullOrEmpty(producerRule.ProducerName))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"The ProducerName property can't be null or empty. This rule will be ignored.", LogLevel.Warn);
                    }
                    else if (UnsupportedMachines.Contains(producerRule.ProducerName) || producerRule.ProducerName.Contains("arecrow"))
                    {
                        if (producerRule.ProducerName == "Crystalarium")
                        {
                            ProducerFrameworkModEntry.ModMonitor.Log($"Producer Framework Mod doesn't support Crystalariums. Use Custom Cristalarium Mod instead.", LogLevel.Warn);
                        }
                        else if (producerRule.ProducerName == "Cask")
                        {
                            ProducerFrameworkModEntry.ModMonitor.Log($"Producer Framework Mod doesn't support Casks. Use Custom Cask Mod instead.", LogLevel.Warn);
                        }
                        else
                        {
                            ProducerFrameworkModEntry.ModMonitor.Log($"Producer Framework Mod doesn't support {producerRule.ProducerName}. This rule will be ignored.", LogLevel.Warn);
                        }
                    }
                    else if (string.IsNullOrEmpty(producerRule.InputIdentifier) && (GetProducerConfig(producerRule.ProducerName)?.NoInputStartMode == null))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"The InputIdentifier property can't be null or empty if there is no config for 'NoInputStartMode' for producer '{producerRule.ProducerName}'. This rule will be ignored.", LogLevel.Warn);
                    }
                    else if ((!string.IsNullOrEmpty(producerRule.InputIdentifier) && GetProducerConfig(producerRule.ProducerName)?.NoInputStartMode != null))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"The InputIdentifier property can't have a value if there is a config for 'NoInputStartMode' for producer '{producerRule.ProducerName}'. This rule will be ignored.", LogLevel.Warn);
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(producerRule.InputIdentifier))
                        {
                            if (producerRule.InputIdentifier == "Stone")
                            {
                                producerRule.InputKey = 390;
                            }
                            else if (!Int32.TryParse(producerRule.InputIdentifier, out var intInputIdentifier))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, producerRule.InputIdentifier));
                                if (pair.Value != null)
                                {
                                    producerRule.InputKey = pair.Key;
                                }
                                else
                                {
                                    producerRule.InputKey = producerRule.InputIdentifier;
                                }
                            }
                            else
                            {
                                producerRule.InputKey = intInputIdentifier;
                            }
                        }

                        if (producerRule.OutputIdentifier != null)
                        {
                            producerRule.OutputConfigs.Add(new OutputConfig()
                            {
                                OutputIdentifier        = producerRule.OutputIdentifier,
                                OutputName              = producerRule.OutputName,
                                OutputTranslationKey    = producerRule.OutputTranslationKey,
                                OutputGenericParentName = producerRule.OutputGenericParentName,
                                OutputGenericParentNameTranslationKey = producerRule.OutputGenericParentNameTranslationKey,
                                PreserveType          = producerRule.PreserveType,
                                KeepInputParentIndex  = producerRule.KeepInputParentIndex,
                                InputPriceBased       = producerRule.InputPriceBased,
                                OutputPriceIncrement  = producerRule.OutputPriceIncrement,
                                OutputPriceMultiplier = producerRule.OutputPriceMultiplier,
                                KeepInputQuality      = producerRule.KeepInputQuality,
                                OutputQuality         = producerRule.OutputQuality,
                                OutputStack           = producerRule.OutputStack,
                                OutputMaxStack        = producerRule.OutputMaxStack,
                                SilverQualityInput    = producerRule.SilverQualityInput,
                                GoldQualityInput      = producerRule.GoldQualityInput,
                                IridiumQualityInput   = producerRule.IridiumQualityInput,
                                OutputColorConfig     = producerRule.OutputColorConfig
                            });
                        }
                        producerRule.OutputConfigs.AddRange(producerRule.AdditionalOutputs);

                        foreach (OutputConfig outputConfig in producerRule.OutputConfigs)
                        {
                            if (!Int32.TryParse(outputConfig.OutputIdentifier, out int outputIndex))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, outputConfig.OutputIdentifier));
                                if (pair.Value != null)
                                {
                                    outputIndex = pair.Key;
                                }
                                else
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"No Output found for '{outputConfig.OutputIdentifier}', producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.", producerRule.WarningsLogLevel);
                                    break;
                                }
                            }
                            outputConfig.OutputIndex = outputIndex;

                            if (outputConfig.OutputName != null)
                            {
                                string customName        = outputConfig.OutputName;
                                string genericParentName = outputConfig.OutputGenericParentName ?? "";
                                if (i18n != null)
                                {
                                    if (outputConfig.OutputTranslationKey != null)
                                    {
                                        string translation = i18n.Get(outputConfig.OutputTranslationKey);
                                        if (!translation.Contains("(no translation:"))
                                        {
                                            customName = translation;
                                        }
                                    }
                                    if (outputConfig.OutputGenericParentNameTranslationKey != null)
                                    {
                                        string translation = i18n.Get(outputConfig.OutputGenericParentNameTranslationKey);
                                        if (!translation.Contains("(no translation:"))
                                        {
                                            genericParentName = translation;
                                        }
                                    }
                                }
                                NameUtils.AddCustomName(outputConfig.OutputIndex, customName);
                                NameUtils.AddGenericParentName(outputConfig.OutputIndex, genericParentName);
                            }

                            foreach (var fuel in outputConfig.RequiredFuel)
                            {
                                if (!Int32.TryParse(fuel.Key, out int fuelIndex))
                                {
                                    KeyValuePair <int, string> pair = objects.FirstOrDefault(o =>
                                                                                             ObjectUtils.IsObjectStringFromObjectName(o.Value, fuel.Key));
                                    if (pair.Value != null)
                                    {
                                        fuelIndex = pair.Key;
                                    }
                                    else
                                    {
                                        ProducerFrameworkModEntry.ModMonitor.Log(
                                            $"No required fuel found for '{fuel.Key}', producer '{producerRule.ProducerName}' and input '{fuel.Key}'. This rule will be ignored.",
                                            producerRule.WarningsLogLevel);
                                        //This is done to abort the rule.
                                        outputConfig.OutputIndex = -1;
                                        break;
                                    }
                                }
                                outputConfig.FuelList.Add(new Tuple <int, int>(fuelIndex, fuel.Value));
                            }
                        }
                        if (producerRule.OutputConfigs.Any(p => p.OutputIndex < 0))
                        {
                            continue;
                        }

                        if (producerRule.FuelIdentifier != null)
                        {
                            producerRule.AdditionalFuel[producerRule.FuelIdentifier] = producerRule.FuelStack;
                        }
                        foreach (var fuel in producerRule.AdditionalFuel)
                        {
                            if (!Int32.TryParse(fuel.Key, out int fuelIndex))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, fuel.Key));
                                if (pair.Value != null)
                                {
                                    fuelIndex = pair.Key;
                                }
                                else
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"No fuel found for '{fuel.Key}', producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.", producerRule.WarningsLogLevel);
                                    break;
                                }
                            }
                            producerRule.FuelList.Add(new Tuple <int, int>(fuelIndex, fuel.Value));
                        }
                        if (producerRule.AdditionalFuel.Count != producerRule.FuelList.Count)
                        {
                            continue;
                        }

                        if (producerRule.PlacingAnimationColorName != null)
                        {
                            try
                            {
                                producerRule.PlacingAnimationColor = DataLoader.Helper.Reflection.GetProperty <Color>(typeof(Color), producerRule.PlacingAnimationColorName).GetValue();
                            }
                            catch (Exception)
                            {
                                ProducerFrameworkModEntry.ModMonitor.Log($"Color '{producerRule.PlacingAnimationColorName}' isn't valid. Check XNA Color Chart for valid names. It'll use the default color '{producerRule.PlacingAnimationColor}'.");
                            }
                        }

                        Tuple <string, object> ruleKey = new Tuple <string, object>(producerRule.ProducerName, producerRule.InputKey);
                        if (RulesRepository.ContainsKey(ruleKey))
                        {
                            ProducerRule oldRule = RulesRepository[ruleKey];
                            if (oldRule.ModUniqueID != producerRule.ModUniqueID)
                            {
                                if (oldRule.OverrideMod.Contains(producerRule.ModUniqueID) && producerRule.OverrideMod.Contains(oldRule.ModUniqueID))
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"Both mod '{oldRule.ModUniqueID}' and '{producerRule.ModUniqueID}' are saying they should override the rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. You should report the problem to these mod's authors. Rule from mod '{oldRule.ModUniqueID}' will be used.", LogLevel.Warn);
                                    continue;
                                }
                                else if (producerRule.OverrideMod.Contains(oldRule.ModUniqueID))
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"Mod '{producerRule.ModUniqueID}' is overriding mod '{oldRule.ModUniqueID}' rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'.", LogLevel.Debug);
                                }
                                else
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"Mod '{producerRule.ModUniqueID}' can't override mod '{oldRule.ModUniqueID}' rule for producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.", LogLevel.Debug);
                                    continue;
                                }
                            }
                        }
                        RulesRepository[ruleKey] = producerRule;
                    }
                }
                catch (Exception e)
                {
                    ProducerFrameworkModEntry.ModMonitor.Log($"Unexpected error for rule from '{producerRule?.ModUniqueID}', producer '{producerRule?.ProducerName}' and input '{producerRule?.InputIdentifier}'. This rule will be ignored.", LogLevel.Error);
                    ProducerFrameworkModEntry.ModMonitor.Log(e.Message + "\n" + e.StackTrace);
                }
            }
        }
        /// <summary>
        /// Adds or replace a list of custom producer rules to the game.
        /// You should probably call this method everytime a save game loads, to ensure all custom objects are properly loaded.
        /// Producer rules should be unique per name of producer and input identifier.
        /// </summary>
        /// <param name="producerRules">A list of producer rules to be added or replaced.</param>
        /// <param name="i18n">Optional i18n object, to look for the output name in case a translation key was used.</param>
        /// <param name="modUniqueId">The mod unique id.</param>
        public static void AddProducerItems(List <ProducerRule> producerRules, ITranslationHelper i18n = null, string modUniqueId = null)
        {
            if (i18n != null)
            {
                ObjectTranslationExtension.TranslationHelpers[modUniqueId] = i18n;
            }
            Dictionary <int, string> objects = DataLoader.Helper.Content.Load <Dictionary <int, string> >("Data\\ObjectInformation", ContentSource.GameContent);

            foreach (var producerRule in producerRules)
            {
                try {
                    producerRule.ModUniqueID = modUniqueId;
                    if (!ValidateRuleProducerName(producerRule.ProducerName))
                    {
                        //Do nothing, already logged.
                    }
                    else if (string.IsNullOrEmpty(producerRule.InputIdentifier) && (GetProducerConfig(producerRule.ProducerName)?.NoInputStartMode == null))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"The InputIdentifier property can't be null or empty if there is no config for 'NoInputStartMode' for producer '{producerRule.ProducerName}'. This rule will be ignored.", LogLevel.Warn);
                    }
                    else if ((!string.IsNullOrEmpty(producerRule.InputIdentifier) && GetProducerConfig(producerRule.ProducerName)?.NoInputStartMode != null))
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"The InputIdentifier property can't have a value if there is a config for 'NoInputStartMode' for producer '{producerRule.ProducerName}'. This rule will be ignored.", LogLevel.Warn);
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(producerRule.InputIdentifier))
                        {
                            if (producerRule.InputIdentifier == "Stone")
                            {
                                producerRule.InputKey = 390;
                            }
                            else if (!Int32.TryParse(producerRule.InputIdentifier, out var intInputIdentifier))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, producerRule.InputIdentifier));
                                if (pair.Value != null)
                                {
                                    producerRule.InputKey = pair.Key;
                                }
                                else
                                {
                                    producerRule.InputKey = producerRule.InputIdentifier;
                                }
                            }
                            else
                            {
                                producerRule.InputKey = intInputIdentifier;
                            }
                        }

                        if (producerRule.OutputIdentifier != null)
                        {
                            producerRule.OutputConfigs.Add(new OutputConfig()
                            {
                                ModUniqueID             = producerRule.ModUniqueID,
                                OutputIdentifier        = producerRule.OutputIdentifier,
                                OutputName              = producerRule.OutputName,
                                OutputTranslationKey    = producerRule.OutputTranslationKey,
                                OutputGenericParentName = producerRule.OutputGenericParentName,
                                OutputGenericParentNameTranslationKey = producerRule.OutputGenericParentNameTranslationKey,
                                PreserveType                = producerRule.PreserveType,
                                KeepInputParentIndex        = producerRule.KeepInputParentIndex,
                                ReplaceWithInputParentIndex = producerRule.ReplaceWithInputParentIndex,
                                InputPriceBased             = producerRule.InputPriceBased,
                                OutputPriceIncrement        = producerRule.OutputPriceIncrement,
                                OutputPriceMultiplier       = producerRule.OutputPriceMultiplier,
                                KeepInputQuality            = producerRule.KeepInputQuality,
                                OutputQuality               = producerRule.OutputQuality,
                                OutputStack         = producerRule.OutputStack,
                                OutputMaxStack      = producerRule.OutputMaxStack,
                                SilverQualityInput  = producerRule.SilverQualityInput,
                                GoldQualityInput    = producerRule.GoldQualityInput,
                                IridiumQualityInput = producerRule.IridiumQualityInput,
                                OutputColorConfig   = producerRule.OutputColorConfig
                            });
                        }
                        producerRule.OutputConfigs.AddRange(producerRule.AdditionalOutputs);

                        foreach (OutputConfig outputConfig in producerRule.OutputConfigs)
                        {
                            outputConfig.ModUniqueID = producerRule.ModUniqueID;
                            if (!Int32.TryParse(outputConfig.OutputIdentifier, out int outputIndex))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, outputConfig.OutputIdentifier));
                                if (pair.Value != null)
                                {
                                    outputIndex = pair.Key;
                                }
                                else
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"No Output found for '{outputConfig.OutputIdentifier}', producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.", producerRule.WarningsLogLevel);
                                    break;
                                }
                            }
                            outputConfig.OutputIndex = outputIndex;


                            foreach (var fuel in outputConfig.RequiredFuel)
                            {
                                if (!Int32.TryParse(fuel.Key, out int fuelIndex))
                                {
                                    KeyValuePair <int, string> pair = objects.FirstOrDefault(o =>
                                                                                             ObjectUtils.IsObjectStringFromObjectName(o.Value, fuel.Key));
                                    if (pair.Value != null)
                                    {
                                        fuelIndex = pair.Key;
                                    }
                                    else
                                    {
                                        ProducerFrameworkModEntry.ModMonitor.Log(
                                            $"No required fuel found for '{fuel.Key}', producer '{producerRule.ProducerName}' and input '{fuel.Key}'. This rule will be ignored.",
                                            producerRule.WarningsLogLevel);
                                        //This is done to abort the rule.
                                        outputConfig.OutputIndex = -1;
                                        break;
                                    }
                                }
                                outputConfig.FuelList.Add(new Tuple <int, int>(fuelIndex, fuel.Value));
                            }
                        }
                        if (producerRule.OutputConfigs.Any(p => p.OutputIndex < 0))
                        {
                            continue;
                        }

                        if (producerRule.FuelIdentifier != null)
                        {
                            producerRule.AdditionalFuel[producerRule.FuelIdentifier] = producerRule.FuelStack;
                        }
                        foreach (var fuel in producerRule.AdditionalFuel)
                        {
                            if (!Int32.TryParse(fuel.Key, out int fuelIndex))
                            {
                                KeyValuePair <int, string> pair = objects.FirstOrDefault(o => ObjectUtils.IsObjectStringFromObjectName(o.Value, fuel.Key));
                                if (pair.Value != null)
                                {
                                    fuelIndex = pair.Key;
                                }
                                else
                                {
                                    ProducerFrameworkModEntry.ModMonitor.Log($"No fuel found for '{fuel.Key}', producer '{producerRule.ProducerName}' and input '{producerRule.InputIdentifier}'. This rule will be ignored.", producerRule.WarningsLogLevel);
                                    break;
                                }
                            }
                            producerRule.FuelList.Add(new Tuple <int, int>(fuelIndex, fuel.Value));
                        }
                        if (producerRule.AdditionalFuel.Count != producerRule.FuelList.Count)
                        {
                            continue;
                        }

                        if (producerRule.PlacingAnimationColorName != null)
                        {
                            try
                            {
                                producerRule.PlacingAnimationColor = DataLoader.Helper.Reflection.GetProperty <Color>(typeof(Color), producerRule.PlacingAnimationColorName).GetValue();
                            }
                            catch (Exception)
                            {
                                ProducerFrameworkModEntry.ModMonitor.Log($"Color '{producerRule.PlacingAnimationColorName}' isn't valid. Check XNA Color Chart for valid names. It'll use the default color '{producerRule.PlacingAnimationColor}'.");
                            }
                        }
                        AddRuleToRepository(producerRule);
                    }
                }
                catch (Exception e)
                {
                    ProducerFrameworkModEntry.ModMonitor.Log($"Unexpected error for rule from '{producerRule?.ModUniqueID}', producer '{producerRule?.ProducerName}' and input '{producerRule?.InputIdentifier}'. This rule will be ignored.", LogLevel.Error);
                    ProducerFrameworkModEntry.ModMonitor.Log(e.Message + "\n" + e.StackTrace);
                }
                foreach (var n in producerRule.AdditionalProducerNames)
                {
                    ProducerRule newProducerRule = null;
                    try {
                        newProducerRule = producerRule.DeepClone();
                        newProducerRule.ProducerName = n;
                        newProducerRule.AdditionalProducerNames.Clear();
                        if (ValidateRuleProducerName(newProducerRule.ProducerName))
                        {
                            AddRuleToRepository(newProducerRule);
                        }
                    }
                    catch (Exception e)
                    {
                        ProducerFrameworkModEntry.ModMonitor.Log($"Unexpected error for rule from '{newProducerRule?.ModUniqueID}', producer '{newProducerRule?.ProducerName}' and input '{newProducerRule?.InputIdentifier}'. This rule will be ignored.", LogLevel.Error);
                        ProducerFrameworkModEntry.ModMonitor.Log(e.Message + "\n" + e.StackTrace);
                    }
                }
                producerRule.AdditionalProducerNames.Clear();
            }
        }