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