/// <summary> /// Stops the upgrade objects from being placable on the ground. /// </summary> /// <param name="__instance"></param> /// <returns></returns> public static void isPlaceable_Postfix(SObject __instance, ref bool __result) { MPMSettings upgradeSettings = ModEntry.GetSettingsFromItem(__instance.name); if (upgradeSettings != null) { __result = false; } }
/// <summary> /// Sets up the mod. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnSaveLoaded(object sender, SaveLoadedEventArgs args) { //Clear out old data if any exists MPMSettings.Clear(); MPMDefinitionSet.Clear(); if (MPMManager != null) { MPMManager.Clear(); } //Load content packs Monitor.Log("Loading content packs...", LogLevel.Info); string filepath = "upgrades.json"; foreach (IContentPack contentPack in Helper.ContentPacks.GetOwned()) { if (contentPack.HasFile(filepath)) { List <MPMSettings> loaded = contentPack.ReadJsonFile <List <MPMSettings> >(filepath); foreach (MPMSettings setting in loaded) { if (MPMSettings.ContainsKey(setting.Key)) { Monitor.Log($"Content pack {contentPack.Manifest.Name} {contentPack.Manifest.Version} tried to add upgrade settings for existing key '{setting.Key}'! " + "Settings left unchanged.", LogLevel.Warn); } else { MPMSettings.Add(setting.Key, setting); } } Monitor.Log($"Loaded content pack {contentPack.Manifest.Name} {contentPack.Manifest.Version} by {contentPack.Manifest.Author}.", LogLevel.Info); } else { Monitor.Log($"Failed reading content pack {contentPack.Manifest.Name} {contentPack.Manifest.Version}: missing file {filepath}.", LogLevel.Warn); } } //Create mail to send recipes MailManager.SetupMail(); //Set up machines to work with PFM Monitor.Log("Defining machines...", LogLevel.Info); MPMDefinitionSet = MassProductionMachineDefinition.Setup(MPMSettings); SeedMakerOverride.Initialize(); //Start manager, loading saved data MPMManager = new MPMManager(); Helper.Events.GameLoop.Saving += OnSave; }
/// <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> /// 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)); } } }
[HarmonyPriority(801)] //Just before ProducerFrameworkMod. Can't use HarmonyBefore attribute, that wasn't working for some reason internal static bool PerformObjectDropInAction(SObject __instance, Item dropInItem, bool probe, Farmer who, ref bool __result) { if (__instance.isTemporarilyInvisible || !(dropInItem is SObject)) { return(false); } SObject input = dropInItem as SObject; bool failLocationCondition = false; bool failSeasonCondition = false; MPMSettings upgradeSettings = ModEntry.GetSettingsFromItem(input.name); if (upgradeSettings != null) { if (!probe) { //Change the machine's mass producer settings MassProductionMachineDefinition mpm = ModEntry.GetMPMMachine(__instance.name, upgradeSettings.Key); if (mpm == null) { Game1.showRedMessage("This cannot take that upgrade."); } else { string oldProducerKey = __instance.GetMassProducerKey(); if (!string.IsNullOrEmpty(oldProducerKey)) { string upgradeItemName = ModEntry.MPMSettings[oldProducerKey].UpgradeObject; JsonAssets.Api jsonAssets = ModEntry.Instance.Helper.ModRegistry.GetApi("spacechase0.JsonAssets") as JsonAssets.Api; int upgradeItemId = jsonAssets.GetObjectId(upgradeItemName); Game1.createItemDebris(new SObject(upgradeItemId, 1), __instance.TileLocation * Game1.tileSize, 0, who.currentLocation); } __instance.SetMassProducerKey(upgradeSettings.Key); input.Stack -= 1; __result = input.Stack <= 0; return(false); } } } else { //Check if this is a valid input for the machine's use if (string.IsNullOrEmpty(__instance.GetMassProducerKey())) { return(true); } if (__instance.heldObject.Value != null && !__instance.name.Equals("Crystalarium") || input.bigCraftable.Value) { return(true); } MassProductionMachineDefinition mpm = ModEntry.GetMPMMachine(__instance.name, __instance.GetMassProducerKey()); if (mpm == null) { return(true); } if (StaticValues.SUPPORTED_VANILLA_MACHINES.ContainsKey(__instance.name)) { IVanillaOverride vanillaOverride = VanillaOverrideList.GetFor(__instance.name); if (vanillaOverride != null) { bool overrideResult = vanillaOverride.Manual_PerformObjectDropInAction(__instance, input, probe, who, mpm); //End early if a result has been found if (overrideResult) { __result = input.Stack <= 0; return(true); } } } ProducerConfig baseConfig = mpm.GetBaseProducerConfig(); GameLocation location = who.currentLocation; if (baseConfig != null) { //TOREVIEW: maybe have machines that can break these conditions? if (!baseConfig.CheckLocationCondition(location)) { failLocationCondition = true; } if (!baseConfig.CheckSeasonCondition()) { failSeasonCondition = true; } if (baseConfig.NoInputStartMode != null) { return(true); } } if (ProducerController.GetProducerItem(__instance.name, input) is ProducerRule producerRule) { if (PFMCompatability.IsInputExcluded(producerRule, mpm, input)) { return(true); } if (__instance.bigCraftable.Value && !probe && __instance.heldObject.Value == null) { __instance.scale.X = 5f; } try { if (failLocationCondition) { throw new RestrictionException("Machine can't be used in this location."); } if (failSeasonCondition) { throw new RestrictionException("Machine can't be used in this season."); } List <InputInfo> inputAndFuelInfo = InputInfo.ConvertPFMInputs(producerRule, input); PFMCompatability.ValidateIfInputsLessThanRequired(producerRule, mpm.Settings, inputAndFuelInfo, who); Dictionary <int, int> fuelQuantities = new Dictionary <int, int>(); foreach (InputInfo inputInfo in inputAndFuelInfo) { if (inputInfo.IsFuel) { fuelQuantities.Add(inputInfo.ID, mpm.Settings.CalculateInputRequired(inputInfo)); } } Func <int, int, bool> fuelSearch = (i, q) => who.hasItemInInventory(i, fuelQuantities[i]); OutputConfig outputConfig = PFMCompatability.ProduceOutput(producerRule, mpm.Settings, __instance, fuelSearch, who, location, baseConfig, input, mpm.Settings.CalculateInputRequired(inputAndFuelInfo.First()), probe, inputInfo: inputAndFuelInfo); if (outputConfig != null) { if (!probe) { foreach (InputInfo inputInfo in inputAndFuelInfo) { if (inputInfo.IsFuel) { RemoveItemsFromInventory(who, inputInfo.ID, mpm.Settings.CalculateInputRequired(inputInfo)); } } List <InputInfo> outputConfigFuels = InputInfo.ConvertPFMInputs(outputConfig); foreach (InputInfo fuel in outputConfigFuels) { RemoveItemsFromInventory(who, fuel.ID, mpm.Settings.CalculateInputRequired(fuel)); } input.Stack -= mpm.Settings.CalculateInputRequired(inputAndFuelInfo.First()); __result = input.Stack <= 0; } else { __result = true; } } } catch (RestrictionException e) { __result = false; if (e.Message != null && !probe && who.IsLocalPlayer) { Game1.showRedMessage(e.Message); } } return(false); } } return(!failLocationCondition && !failSeasonCondition); }
/// <summary> /// Constructor. /// </summary> /// <param name="baseProducerName">Name of the base production machine as used by PFM or the base game.</param> /// <param name="settings">How the machine affects inputs and outputs.</param> protected MassProductionMachineDefinition(string baseProducerName, MPMSettings settings) { BaseProducerName = baseProducerName; Settings = settings; BlacklistedInputKeys = new List <object>(); }