Skip to content

Commit

Permalink
Merge pull request #763 from kzawora/feature/transfer_seeds_to_junimo…
Browse files Browse the repository at this point in the history
…_huts

[Automate] Add option to transfer seeds and fertilizers into Junimo huts
  • Loading branch information
Pathoschild authored Oct 10, 2022
2 parents 2fbf86c + 06ee523 commit 35a00ac
Show file tree
Hide file tree
Showing 18 changed files with 372 additions and 145 deletions.
18 changes: 15 additions & 3 deletions Automate/Framework/AutomationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,21 @@ public AutomationFactory(Func<ModConfig> config, IMonitor monitor, IReflectionHe

case JunimoHut hut:
{
var config = this.Config();
bool betterJunimosCompat = config.ModCompatibility.BetterJunimos && this.IsBetterJunimosLoaded;
return new JunimoHutMachine(hut, location, ignoreSeedOutput: betterJunimosCompat, ignoreFertilizerOutput: betterJunimosCompat, pullGemstonesFromJunimoHuts: config.PullGemstonesFromJunimoHuts);
ModConfig config = this.Config();

JunimoHutBehavior gemBehavior = config.JunimoHutBehaviorForGemStones;
if (gemBehavior is JunimoHutBehavior.AutoDetect)
gemBehavior = JunimoHutBehavior.Ignore;

JunimoHutBehavior fertilizerBehavior = config.JunimoHutBehaviorForFertilizer;
if (fertilizerBehavior is JunimoHutBehavior.AutoDetect)
fertilizerBehavior = this.IsBetterJunimosLoaded ? JunimoHutBehavior.Ignore : JunimoHutBehavior.MoveIntoChests;

JunimoHutBehavior seedBehavior = config.JunimoHutBehaviorForFertilizer;
if (seedBehavior is JunimoHutBehavior.AutoDetect)
seedBehavior = this.IsBetterJunimosLoaded ? JunimoHutBehavior.Ignore : JunimoHutBehavior.MoveIntoChests;

return new JunimoHutMachine(hut, location, gemBehavior, fertilizerBehavior, seedBehavior);
}

case Mill mill:
Expand Down
72 changes: 54 additions & 18 deletions Automate/Framework/GenericModConfigMenuIntegrationForAutomate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ public void Register()
get: config => config.Enabled,
set: (config, value) => config.Enabled = value
)
.AddCheckbox(
name: I18n.Config_JunimoHutsOutputGems_Name,
tooltip: I18n.Config_JunimoHutsOutputGems_Desc,
get: config => config.PullGemstonesFromJunimoHuts,
set: (config, value) => config.PullGemstonesFromJunimoHuts = value
)
.AddNumberField(
name: I18n.Config_AutomationInterval_Name,
tooltip: I18n.Config_AutomationInterval_Desc,
Expand All @@ -76,22 +70,12 @@ public void Register()
tooltip: I18n.Config_ToggleOverlayKey_Desc,
get: config => config.Controls.ToggleOverlay,
set: (config, value) => config.Controls.ToggleOverlay = value
);

// mod compatibility
menu
.AddSectionTitle(I18n.Config_Title_ModCompatibility)
.AddCheckbox(
name: I18n.Config_BetterJunimos_Name,
tooltip: I18n.Config_BetterJunimos_Desc,
get: config => config.ModCompatibility.BetterJunimos,
set: (config, value) => config.ModCompatibility.BetterJunimos = value
)
.AddCheckbox(
name: I18n.Config_WarnForMissingBridgeMod_Name,
tooltip: I18n.Config_WarnForMissingBridgeMod_Desc,
get: config => config.ModCompatibility.WarnForMissingBridgeMod,
set: (config, value) => config.ModCompatibility.WarnForMissingBridgeMod = value
get: config => config.WarnForMissingBridgeMod,
set: (config, value) => config.WarnForMissingBridgeMod = value
);

// connectors
Expand All @@ -114,6 +98,30 @@ public void Register()
set: (config, value) => this.SetCustomConnectors(config, value.Split(',').Select(p => p.Trim()))
);

// Junimo huts
menu.AddSectionTitle(I18n.Config_Title_JunimoHuts);
this.AddJunimoHutBehaviorDropdown(
menu,
name: I18n.Config_JunimoHutGems_Name,
tooltip: I18n.Config_JunimoHutGems_Desc,
get: config => config.JunimoHutBehaviorForGemStones,
set: (config, value) => config.JunimoHutBehaviorForGemStones = value
);
this.AddJunimoHutBehaviorDropdown(
menu,
name: I18n.Config_JunimoHutFertilizer_Name,
tooltip: I18n.Config_JunimoHutFertilizer_Desc,
get: config => config.JunimoHutBehaviorForFertilizer,
set: (config, value) => config.JunimoHutBehaviorForFertilizer = value
);
this.AddJunimoHutBehaviorDropdown(
menu,
name: I18n.Config_JunimoHutSeeds_Name,
tooltip: I18n.Config_JunimoHutSeeds_Desc,
get: config => config.JunimoHutBehaviorForSeeds,
set: (config, value) => config.JunimoHutBehaviorForSeeds = value
);

// machine overrides
menu.AddSectionTitle(I18n.Config_Title_MachineOverrides);
foreach (var entry in this.Data.DefaultMachineOverrides)
Expand All @@ -132,6 +140,34 @@ public void Register()
/*********
** Private methods
*********/
/****
** Junimo huts
****/
/// <summary>Add a dropdown to configure Junimo hut behavior for an item type.</summary>
/// <param name="menu">The config menu to extend.</param>
/// <param name="name">The label text to show in the form.</param>
/// <param name="tooltip">The tooltip text shown when the cursor hovers on the field.</param>
/// <param name="get">Get the current value from the mod config.</param>
/// <param name="set">Set a new value in the mod config.</param>
private void AddJunimoHutBehaviorDropdown(GenericModConfigMenuIntegration<ModConfig> menu, Func<string> name, Func<string> tooltip, Func<ModConfig, JunimoHutBehavior> get, Action<ModConfig, JunimoHutBehavior> set)
{
menu.AddDropdown(
name: name,
tooltip: tooltip,
get: config => get(config).ToString(),
set: (config, value) => set(config, Enum.Parse<JunimoHutBehavior>(value)),
allowedValues: Enum.GetNames<JunimoHutBehavior>(),
formatAllowedValue: value => value switch
{
nameof(JunimoHutBehavior.AutoDetect) => I18n.Config_JunimoHuts_AutoDetect(),
nameof(JunimoHutBehavior.Ignore) => I18n.Config_JunimoHuts_Ignore(),
nameof(JunimoHutBehavior.MoveIntoChests) => I18n.Config_JunimoHuts_MoveIntoChests(),
nameof(JunimoHutBehavior.MoveIntoHut) => I18n.Config_JunimoHuts_MoveIntoHuts(),
_ => "???" // should never happen
}
);
}

/****
** Connectors
****/
Expand Down
110 changes: 85 additions & 25 deletions Automate/Framework/Machines/Buildings/JunimoHutMachine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Pathoschild.Stardew.Automate.Framework.Models;
using StardewValley;
using StardewValley.Buildings;
using StardewValley.Objects;
Expand All @@ -12,14 +13,20 @@ internal class JunimoHutMachine : BaseMachineForBuilding<JunimoHut>
/*********
** Fields
*********/
/// <summary>Whether seeds should be ignored when selecting output.</summary>
private readonly bool IgnoreSeedOutput;
/// <summary>How to handle gem stones in the hut or connected chests.</summary>
private readonly JunimoHutBehavior GemBehavior;

/// <summary>Whether fertilizer should be ignored when selecting output.</summary>
private readonly bool IgnoreFertilizerOutput;
/// <summary>How to handle fertilizer in the hut or connected chests.</summary>
private readonly JunimoHutBehavior FertilizerBehavior;

/// <summary>Whether to pull gemstones out of Junimo huts.</summary>
public bool PullGemstonesFromJunimoHuts { get; set; }
/// <summary>How to handle seeds in the hut or connected chests.</summary>
private readonly JunimoHutBehavior SeedBehavior;

/// <summary>Whether the Junimo hut can automate input.</summary>
private readonly bool HasInput;

/// <summary>Whether any items are configured to be skipped when outputting.</summary>
private readonly bool HasIgnoredOutput;

/// <summary>The Junimo hut's output chest.</summary>
private Chest Output => this.Machine.output.Value;
Expand All @@ -31,15 +38,24 @@ internal class JunimoHutMachine : BaseMachineForBuilding<JunimoHut>
/// <summary>Construct an instance.</summary>
/// <param name="hut">The underlying Junimo hut.</param>
/// <param name="location">The location which contains the machine.</param>
/// <param name="ignoreSeedOutput">Whether seeds should be ignored when selecting output.</param>
/// <param name="ignoreFertilizerOutput">Whether fertilizer should be ignored when selecting output.</param>
/// <param name="pullGemstonesFromJunimoHuts">Whether to pull gemstones out of Junimo huts.</param>
public JunimoHutMachine(JunimoHut hut, GameLocation location, bool ignoreSeedOutput, bool ignoreFertilizerOutput, bool pullGemstonesFromJunimoHuts)
/// <param name="gemBehavior">How to handle gem stones in the hut or connected chests.</param>
/// <param name="fertilizerBehavior">How to handle fertilizer in the hut or connected chests.</param>
/// <param name="seedBehavior">How to handle seeds in the hut or connected chests.</param>
public JunimoHutMachine(JunimoHut hut, GameLocation location, JunimoHutBehavior gemBehavior, JunimoHutBehavior fertilizerBehavior, JunimoHutBehavior seedBehavior)
: base(hut, location, BaseMachine.GetTileAreaFor(hut))
{
this.IgnoreSeedOutput = ignoreSeedOutput;
this.IgnoreFertilizerOutput = ignoreFertilizerOutput;
this.PullGemstonesFromJunimoHuts = pullGemstonesFromJunimoHuts;
this.GemBehavior = gemBehavior;
this.FertilizerBehavior = fertilizerBehavior;
this.SeedBehavior = seedBehavior;

this.HasInput =
gemBehavior is JunimoHutBehavior.MoveIntoHut
|| fertilizerBehavior is JunimoHutBehavior.MoveIntoHut
|| seedBehavior is JunimoHutBehavior.MoveIntoHut;
this.HasIgnoredOutput =
gemBehavior is not JunimoHutBehavior.MoveIntoChests
|| fertilizerBehavior is not JunimoHutBehavior.MoveIntoChests
|| seedBehavior is not JunimoHutBehavior.MoveIntoChests;
}

/// <summary>Get the machine's processing state.</summary>
Expand All @@ -48,8 +64,11 @@ public override MachineState GetState()
if (this.Machine.isUnderConstruction())
return MachineState.Disabled;

return this.GetNextOutput() != null
? MachineState.Done
if (this.GetNextOutput() != null)
return MachineState.Done;

return this.HasInput
? MachineState.Empty
: MachineState.Processing;
}

Expand All @@ -64,7 +83,41 @@ public override MachineState GetState()
/// <returns>Returns whether the machine started processing an item.</returns>
public override bool SetInput(IStorage input)
{
return false; // no input
if (!this.HasInput)
return false;

// get next item
ITrackedStack? tracker = null;
foreach (ITrackedStack stack in input.GetItems())
{
if (stack.Sample is not SObject obj)
continue;

switch (obj.Category)
{
case SObject.SeedsCategory when this.SeedBehavior == JunimoHutBehavior.MoveIntoHut:
tracker = stack;
break;

case SObject.fertilizerCategory when this.FertilizerBehavior == JunimoHutBehavior.MoveIntoHut:
tracker = stack;
break;

case (SObject.GemCategory or SObject.mineralsCategory) when this.GemBehavior == JunimoHutBehavior.MoveIntoHut:
tracker = stack;
break;
}

if (tracker is not null)
break;
}
if (tracker == null)
return false;

// place item in output chest
SObject item = (SObject)tracker.Take(1)!;
this.Output.addItem(item);
return true;
}


Expand All @@ -84,15 +137,22 @@ private void OnOutputTaken(Item item)
{
foreach (Item item in this.Output.items.Where(p => p != null))
{
// ignore gems which change Junimo colors (see JunimoHut:getGemColor)
if (!this.PullGemstonesFromJunimoHuts && item.Category is SObject.GemCategory or SObject.mineralsCategory)
continue;

// ignore items used by another mod
if (this.IgnoreSeedOutput && item.Category == SObject.SeedsCategory)
continue;
if (this.IgnoreFertilizerOutput && item.Category == SObject.fertilizerCategory)
continue;
if (this.HasIgnoredOutput)
{
bool ignore = false;

switch (item.Category)
{
case SObject.SeedsCategory when this.SeedBehavior is not JunimoHutBehavior.MoveIntoChests:
case SObject.fertilizerCategory when this.FertilizerBehavior is not JunimoHutBehavior.MoveIntoChests:
case (SObject.GemCategory or SObject.mineralsCategory) when this.GemBehavior is not JunimoHutBehavior.MoveIntoChests:
ignore = true;
break;
}

if (ignore)
continue;
}

return item;
}
Expand Down
18 changes: 18 additions & 0 deletions Automate/Framework/Models/JunimoHutBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Pathoschild.Stardew.Automate.Framework.Models
{
/// <summary>Indicates how Junimo huts should be automated for a specific item type.</summary>
internal enum JunimoHutBehavior
{
/// <summary>Apply the default logic based on the player's installed mods (e.g. leave seeds in the hut if Better Junimos is installed).</summary>
AutoDetect,

/// <summary>Ignore items of this type, so they're not transferred either way.</summary>
Ignore,

/// <summary>Move any items of this type from the Junimo Hut into connected chests.</summary>
MoveIntoChests,

/// <summary>Move any items of this type from connected chests into the Junimo Hut.</summary>
MoveIntoHut
}
}
15 changes: 0 additions & 15 deletions Automate/Framework/Models/ModCompatibilityConfig.cs

This file was deleted.

18 changes: 13 additions & 5 deletions Automate/Framework/Models/ModConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ internal class ModConfig
/// <summary>Whether Automate is enabled.</summary>
public bool Enabled { get; set; } = true;

/// <summary>Whether to pull gemstones out of Junimo huts. If true, you won't be able to change Junimo colors by placing gemstones in their hut.</summary>
public bool PullGemstonesFromJunimoHuts { get; set; } = false;
/// <summary>How Junimo huts should automate gemstones.</summary>
/// <remarks>The <see cref="JunimoHutBehavior.AutoDetect"/> option is equivalent to <see cref="JunimoHutBehavior.Ignore"/>.</remarks>
public JunimoHutBehavior JunimoHutBehaviorForGemStones { get; set; } = JunimoHutBehavior.AutoDetect;

/// <summary>How Junimo huts should automate fertilizer items.</summary>
/// <remarks>The <see cref="JunimoHutBehavior.AutoDetect"/> option is equivalent to <see cref="JunimoHutBehavior.Ignore"/> (if Better Junimos is installed), else <see cref="JunimoHutBehavior.MoveIntoChests"/>.</remarks>
public JunimoHutBehavior JunimoHutBehaviorForFertilizer { get; set; } = JunimoHutBehavior.AutoDetect;

/// <summary>How Junimo huts should automate seed items.</summary>
/// <remarks>The <see cref="JunimoHutBehavior.AutoDetect"/> option is equivalent to <see cref="JunimoHutBehavior.Ignore"/> (if Better Junimos is installed), else <see cref="JunimoHutBehavior.MoveIntoChests"/>.</remarks>
public JunimoHutBehavior JunimoHutBehaviorForSeeds { get; set; } = JunimoHutBehavior.AutoDetect;

/// <summary>The number of ticks between each automation process (60 = once per second).</summary>
public int AutomationInterval { get; set; } = 60;
Expand All @@ -27,8 +36,8 @@ internal class ModConfig
/// <summary>The in-game object names through which machines can connect.</summary>
public HashSet<string> ConnectorNames { get; set; } = new() { "Workbench" };

/// <summary>Options affecting compatibility with other mods.</summary>
public ModCompatibilityConfig ModCompatibility { get; set; } = new();
/// <summary>Whether to log a warning if the player installs a custom-machine mod that requires a separate compatibility patch which isn't installed.</summary>
public bool WarnForMissingBridgeMod { get; set; } = true;

/// <summary>The configuration for specific machines by ID.</summary>
public Dictionary<string, ModConfigMachine> MachineOverrides { get; set; } = new();
Expand All @@ -47,7 +56,6 @@ public void OnDeserialized(StreamingContext context)
// normalize
this.Controls ??= new();
this.ConnectorNames = this.ConnectorNames.ToNonNullCaseInsensitive();
this.ModCompatibility ??= new();
this.MachineOverrides = this.MachineOverrides.ToNonNullCaseInsensitive();

// remove null values
Expand Down
2 changes: 1 addition & 1 deletion Automate/ModEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public override void Entry(IModHelper helper)

// log info
this.Monitor.VerboseLog($"Initialized with automation every {this.Config.AutomationInterval} ticks.");
if (this.Config.ModCompatibility.WarnForMissingBridgeMod)
if (this.Config.WarnForMissingBridgeMod)
this.ReportMissingBridgeMods(this.Data.SuggestedIntegrations);
}

Expand Down
Loading

0 comments on commit 35a00ac

Please sign in to comment.