/// <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); }
/// <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); }
/// <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; }
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); }
/// <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) } )); } }
/// <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); }
/// <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); }
/// <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(); } }